1. 程式人生 > >redis遷移相關問題

redis遷移相關問題

現象

1183:M 17 Sep 13:54:54.069 * Slave 172.17.1.49:7480 asks for synchronization
1183:M 17 Sep 13:54:54.070 * Full resync requested by slave 172.17.1.49:7480
1183:M 17 Sep 13:54:54.070 * Starting BGSAVE for SYNC with target: disk
1183:M 17 Sep 13:54:54.070 # Can't save in background: fork: Cannot allocate memory
#無法分配記憶體
1183:M 17 Sep 13:54:54.070 # BGSAVE for replication failed

解決

這個錯誤是因為,redis有個預設選項 stop-writes-on-bgsave-error yes 在預設情況下,如果rdb snapshots持久化出現問題,設定這個引數後,redis不允許使用者進行任何更新 不徹底的解決方式,將這個選項改為no stop-writes-on-bgsave-error no 這樣只是當redis寫硬碟快照出錯時,可以讓使用者繼續做更新操作,但是寫硬碟仍然是失敗的。 徹底解決方式 編輯/etc/sysctl.conf新增 vm.overcommit_memory=1 執行sysctrl -p 使其生效

Redis的資料回寫機制分同步和非同步兩種,

    同步回寫即SAVE命令,主程序直接向磁盤迴寫資料。在資料大的情況下會導致系統假死很長時間,所以一般不是推薦的。

    非同步回寫即BGSAVE命令,主程序fork後,複製自身並通過這個新的程序回寫磁碟,回寫結束後新程序自行關閉。由於這樣做不需要主程序阻塞,系統不會假死,一般預設會採用這個方法

    預設採用方式2,所以如果我們要將資料刷到硬碟上,這時redis分配記憶體不能太大,否則很容易發生記憶體不夠用無法fork的問題; 設定一個合理的寫磁碟策略,否則寫頻繁的應用,也會導致頻繁的fork操作,對於佔用了大記憶體的redis來說,fork消耗資源的代價是很大的;

原因

在什麼條件下,linux會發現記憶體不足呢? 在Linux下有個CommitLimit 用於限制系統應用使用的記憶體資源: [[email protected] bin]# grep -i commit /proc/meminfo CommitLimit:      961120 kB Committed_AS:    1514856 kB 其中: CommitLimit是一個記憶體分配上限, Committed_AS是已經分配的記憶體大小。 虛擬記憶體演算法:  

  1. CommitLimit = 實體記憶體 * overcommit_ratio(/proc/sys/vm/overcmmit_ratio,預設50,即50%) + swap大小  

它是由核心引數overcommit_ratio的控制的,當我們的應用申請記憶體的時候,當出現以下情況:  

  1. 應用程式要申請的記憶體 + 系統已經分配的記憶體(也就是Committed_AS)> CommitLimit  

這時候就是記憶體不足,到了這裡,作業系統要怎麼辦,就要祭出我們的主角“overcommit_memory”引數了(/proc/sys/vm/overcommit_memory);  

  • vm.overcommit_memory = 0   啟發策略

比較 此次請求分配的虛擬記憶體大小和系統當前空閒的實體記憶體加上swap,決定是否放行。系統在為應用程序分配虛擬地址空間時,會判斷當前申請的虛擬地址空間大小是否超過剩餘記憶體大小,如果超過,則虛擬地址空間分配失敗。因此,也就是如果程序本身佔用的虛擬地址空間比較大或者剩餘記憶體比較小時,fork、malloc等呼叫可能會失敗。  

  •  vm.overcommit_memory = 1 允許overcommit

直接放行,系統在為應用程序分配虛擬地址空間時,完全不進行限制,這種情況下,避免了fork可能產生的失敗,但由於malloc是先分配虛擬地址空間,而後通過異常陷入核心分配真正的實體記憶體,在記憶體不足的情況下,這相當於完全遮蔽了應用程序對系統記憶體狀態的感知,即malloc總是能成功,一旦記憶體不足,會引起系統OOM殺程序,應用程式對於這種後果是無法預測的。  

  • vm.overcommit_memory = 2 禁止overcommit

根據系統記憶體狀態確定了虛擬地址空間的上限,由於很多情況下,程序的虛擬地址空間佔用遠大於其實際佔用的實體記憶體,這樣一旦記憶體使用量上去以後,對於一些動態產生的程序(需要複製父程序地址空間)則很容易建立失敗,如果業務過程沒有過多的這種動態申請記憶體或者建立子程序,則影響不大,否則會產生比較大的影響 。這種情況下系統所能分配的記憶體不會超過上面提到的CommitLimit大小,如果這麼多資源已經用光,那麼後面任何嘗試申請記憶體的行為都會返回錯誤,這通常意味著此時沒法執行任何新程式。

在小記憶體的程序上做fork, 不需要太多資源. 但當這個程序的記憶體空間以G為單位時, fork就成為一件很恐怖的操作. 在16G記憶體的足跡上fork 14G的程序, 系統肯定Cannot allocate memory. 主機的Redis 改動的越頻繁 fork程序也越頻繁, 所以一直在Cannot allocate memory

引數

Linux核心會根據引數vm.overcommit_memory引數的設定決定是否放行。

vm.overcommit_memory = 1:直接放行

vm.overcommit_memory = 0:則比較 此次請求分配的虛擬記憶體大小和系統當前空閒的實體記憶體加上swap,決定是否放行。

vm.overcommit_memory = 2:則會比較 程序所有已分配的虛擬記憶體加上此次請求分配的虛擬記憶體和系統當前的空閒實體記憶體加上swap,決定是否放行。

linux設定vm.overcommit_memory 方法,永久性修改核心引數

在/etc/sysctl.conf檔案裡面加入或者直接刪除也可以,因為它預設值就是0

vm.overcommit_memory = 0執行使之生效