您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“Redis持久化時(shí)內(nèi)存不足怎么處理”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Redis持久化時(shí)內(nèi)存不足怎么處理”吧!
# Java錯(cuò)誤日志: redis.clients.jedis.exceptions.JedisDataException: MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error. # Redis錯(cuò)誤日志: Can't save in background: fork: Resource temporaily unavailable # 或 Can’t save in background: fork: Cannot allocate memory
Redis
在個(gè)默認(rèn)情況下,如果在RDB snapshots
持久化過程中出現(xiàn)問題,Redis
不允許用戶進(jìn)行任何更新操作;即:stop-writes-on-bgsave-error yes
。
臨時(shí)解決方案是通過命令:config set stop-writes-on-bgsave-error no
設(shè)置這個(gè)選項(xiàng)為false
,讓程序忽略了這個(gè)異常,使得程序能夠繼續(xù)往下運(yùn)行,但寫硬盤仍然是失敗的!
Redis
在進(jìn)行持久化的時(shí)候,有的時(shí)候可以在日志中看到fork進(jìn)程失敗的提示,一般是系統(tǒng)可用的內(nèi)存空間不夠?qū)е?,這需要我們對fork
原理明白,才能更好的進(jìn)行參數(shù)調(diào)整。
一般來說Redis
在進(jìn)行RDB
的時(shí)候,會fork
出一個(gè)子進(jìn)程,子進(jìn)程和父進(jìn)程會共享一個(gè)地址空間,在fork
子進(jìn)程的時(shí)候,會檢查當(dāng)前機(jī)器可用的內(nèi)存是否滿足fork
出一個(gè)子進(jìn)程的要求,一般由操作系統(tǒng)overcommit_memory
(系統(tǒng)內(nèi)存分配策略)決定。
Redis
的數(shù)據(jù)回寫機(jī)制分同步和異步兩種,
同步回寫即SAVE
命令,主進(jìn)程直接向磁盤回寫數(shù)據(jù)。在數(shù)據(jù)大的情況下會導(dǎo)致系統(tǒng)假死很長時(shí)間,所以一般不是推薦的。
異步回寫即BGSAVE
命令,主進(jìn)程fork
后,復(fù)制自身并通過這個(gè)新的進(jìn)程回寫磁盤,回寫結(jié)束后新進(jìn)程自行關(guān)閉。由于這樣做不需要主進(jìn)程阻塞,系統(tǒng)不會假死,一般默認(rèn)會采用這個(gè)方法。
Redis
默認(rèn)采用異步回寫,所以如果我們要將數(shù)據(jù)刷到硬盤上,這時(shí)Redis
分配內(nèi)存不能太大,否則很容易發(fā)生內(nèi)存不夠用無法fork
的問題; 設(shè)置一個(gè)合理的寫磁盤策略,否則寫頻繁的應(yīng)用,也會導(dǎo)致頻繁的fork
操作,對于占用了大內(nèi)存的Redis
來說,fork
消耗資源的代價(jià)是很大的;
Linux
對大部分申請內(nèi)存的請求都回復(fù)yes
,以便能跑更多更大的程序。
因?yàn)樯暾垉?nèi)存后,并不會馬上使用內(nèi)存,將這些不會使用的空閑內(nèi)存分配給其它程序使用,以提高內(nèi)存利用率,這種技術(shù)叫做Overcommit
。
一般情況下,當(dāng)所有程序都不會用到自己申請的所有內(nèi)存時(shí),系統(tǒng)不會出問題,但是如果程序隨著運(yùn)行,需要的內(nèi)存越來越大,在自己申請的大小范圍內(nèi),不斷占用更多內(nèi)存,直到超出物理內(nèi)存,當(dāng)Linux
發(fā)現(xiàn)內(nèi)存不足時(shí),會發(fā)生OOM killer(OOM=out-of-memory)
。
OOM killer
會選擇殺死一些進(jìn)程,以便釋放內(nèi)存。當(dāng)發(fā)生OOM killer
時(shí),會記錄在系統(tǒng)日志中/var/log/messages
。
用戶態(tài)進(jìn)程,非內(nèi)核線程,占用內(nèi)存越多和運(yùn)行時(shí)間越短的進(jìn)程越有可能被殺掉。
在Linux
下有個(gè)vm內(nèi)核參數(shù):CommitLimit
用于限制系統(tǒng)應(yīng)用使用的內(nèi)存資源;執(zhí)行grep -i commit /proc/meminfo
,看到CommitLimit
和Committed_As
參數(shù)。
CommitLimit
是一個(gè)內(nèi)存分配上限,CommitLimit = 物理內(nèi)存 * overcommit_ratio(/proc/sys/vm/overcmmit_ratio,默認(rèn)50,即50%) + swap大小
Committed_As
是已經(jīng)分配的內(nèi)存大小(應(yīng)用程序要申請的內(nèi)存 + 系統(tǒng)已經(jīng)分配的內(nèi)存)。
vm.overcommit_memory
文件指定了內(nèi)核針對內(nèi)存分配的策略,其值可以是0、1、2
。
0
:啟發(fā)策略(默認(rèn));表示內(nèi)核將檢查是否有足夠的可用內(nèi)存供應(yīng)用進(jìn)程使用;如果有足夠的可用內(nèi)存,內(nèi)存申請?jiān)试S;否則,內(nèi)存申請失敗,并把錯(cuò)誤返回給應(yīng)用進(jìn)程。系統(tǒng)在為應(yīng)用進(jìn)程分配虛擬地址空間時(shí),會判斷當(dāng)前申請的虛擬地址空間大小是否超過剩余內(nèi)存大小,如果超過,則虛擬地址空間分配失敗。因此,也就是如果進(jìn)程本身占用的虛擬地址空間比較大或者剩余內(nèi)存比較小時(shí),fork
、malloc
等調(diào)用可能會失敗。 0
即是啟發(fā)式的overcommitting handle
,會盡量減少swap
交換分區(qū)的使用,root
可以分配比一般用戶略多的內(nèi)存。
1
:允許overcommit
;表示內(nèi)核允許分配所有的物理內(nèi)存,而不管當(dāng)前的內(nèi)存狀態(tài)如何,允許超過CommitLimit
,這種情況下,避免了fork
可能產(chǎn)生的失敗,但由于malloc
是先分配虛擬地址空間,而后通過異常陷入內(nèi)核分配真正的物理內(nèi)存,在內(nèi)存不足的情況下,這相當(dāng)于完全屏蔽了應(yīng)用進(jìn)程對系統(tǒng)內(nèi)存狀態(tài)的感知,即malloc
總是能成功,一旦內(nèi)存不足,會引起系統(tǒng)OOM
殺進(jìn)程,應(yīng)用程序?qū)τ谶@種后果是無法預(yù)測的。 直至內(nèi)存用完為止。在數(shù)據(jù)庫服務(wù)器上不建議設(shè)置為1,從而盡量避免使用swap
交換分區(qū)。
2
:禁止overcommit
;表示不允許超過CommitLimit
值。由于很多情況下,進(jìn)程的虛擬地址空間占用遠(yuǎn)大于其實(shí)際占用的物理內(nèi)存,這樣一旦內(nèi)存使用量上去以后,對于一些動(dòng)態(tài)產(chǎn)生的進(jìn)程(需要復(fù)制父進(jìn)程地址空間)則很容易創(chuàng)建失敗,如果業(yè)務(wù)過程沒有過多的這種動(dòng)態(tài)申請內(nèi)存或者創(chuàng)建子進(jìn)程,則影響不大,否則會產(chǎn)生比較大的影響 。這種情況下系統(tǒng)所能分配的內(nèi)存不會超過上面提到的CommitLimit
大小,如果這么多資源已經(jīng)用光,那么后面任何嘗試申請內(nèi)存的行為都會返回錯(cuò)誤,這通常意味著此時(shí)沒法運(yùn)行任何新程序。
我們可以通過設(shè)置overcommit_memory=1
的優(yōu)化,減少操作系統(tǒng)內(nèi)存,提高Redis
的fork
成功率,因?yàn)?code>fork后的進(jìn)程和父進(jìn)程共享一個(gè)數(shù)據(jù)空間,持久化要新增的內(nèi)存空間都會小于父進(jìn)程已經(jīng)使用的空間,具體有三種方式修改內(nèi)核參數(shù),但要有root
權(quán)限:
編輯/etc/sysctl.conf
,改vm.overcommit_memory=1
,然后sysctl -p
使配置文件生效;
命令:sysctl vm.overcommit_memory=1
;
命令:echo 1 > /proc/sys/vm/overcommit_memory
;
當(dāng)Redis
持久化fork
子進(jìn)程后,占用內(nèi)存大小和父進(jìn)程等同,由于Linux
在寫時(shí)有copy-on-write
機(jī)制,父子進(jìn)程共享相同的物理內(nèi)存頁,當(dāng)父進(jìn)程處理寫請求的時(shí)候會把要修改的頁創(chuàng)建副本,而子進(jìn)程在fork
過程中共享整個(gè)父進(jìn)程的內(nèi)存快照。如果我們要減少創(chuàng)建的副本的大小,就涉及操作系統(tǒng)的另外一個(gè)概念Huge Pages
(大頁)。
在Redhat Linux
中,內(nèi)存都是以頁的形式劃分的,默認(rèn)情況下每頁是4K
,這就意味著如果物理內(nèi)存很大,則映射表的條目將會非常多,會影響CPU
的檢索效率。因?yàn)閮?nèi)存大小是固定的,為了減少映射表的條目,可采取的辦法只有增加頁的尺寸。Linux Kernel
在2.6.38
內(nèi)核中增加了THP
(Transparent Huge Pages)的特性,支持大內(nèi)存頁(2MB
)分配,默認(rèn)開啟。當(dāng)開啟后可以加快fork
子進(jìn)程的速度,但fork
操作之后,每個(gè)內(nèi)存頁從原來的4KB
變成了2MB
,會大幅增加重寫期間父進(jìn)程內(nèi)存消耗,同時(shí)每次寫命令引起的復(fù)制內(nèi)存頁單位放大了512
倍,會拖慢寫操作的執(zhí)行時(shí)間,因此在使用Redis
的時(shí)候Redis
建議關(guān)閉THP
,方法為:echo never > /sys/kernel/mm/transparent_hugepage/enabled
。為了讓機(jī)器重啟該參數(shù)仍然生效,建議在/etc/rc.local
中追加echo never > /sys/kernel/mm/transparent_hugepage/enabled
,避免失效。當(dāng)大頁被關(guān)閉后,可以看到同等操作下,RDB
備份時(shí)候的copy-on-write
變化內(nèi)存空間會減少。
綜上分析,我們可以操作系統(tǒng)物理內(nèi)存和Redis
內(nèi)存之間的一些關(guān)系,尤其Redis
在持久化的時(shí)候fork
進(jìn)程會隨操作系統(tǒng)的參數(shù)不同,需要的內(nèi)存也有所不同,為了加快fork
子進(jìn)程的速度以及主備之間的文件傳輸同步,一般我們建議一個(gè)Redis
節(jié)點(diǎn)的最大內(nèi)存在10G-15G
左右,操作系統(tǒng)的內(nèi)存適當(dāng)冗余,盡量控制同一臺機(jī)器的多個(gè)Redis
節(jié)點(diǎn)在同一個(gè)時(shí)間點(diǎn)進(jìn)行RDB
備份(可以通過緩存中心定時(shí)備份),導(dǎo)致內(nèi)存同一時(shí)刻增加避免內(nèi)存空間不足導(dǎo)致的fork
失敗,最安全保險(xiǎn)的情況是內(nèi)存為Redis
的2倍
,但是在vm.overcommit_memory=1和大頁關(guān)閉的情況下,可以根據(jù)實(shí)際使用,降低操作系統(tǒng)的整個(gè)內(nèi)存大小 。
參考文章:
https://www.jianshu.com/p/785ee3bea266
https://www.cnblogs.com/wjoyxt/p/3777042.html
到此,相信大家對“Redis持久化時(shí)內(nèi)存不足怎么處理”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。