本篇內(nèi)容主要講解“redis持久化時(shí)內(nèi)存不足怎么處理”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“Redis持久化時(shí)內(nèi)存不足怎么處理”吧!
超過(guò)10余年行業(yè)經(jīng)驗(yàn),技術(shù)領(lǐng)先,服務(wù)至上的經(jīng)營(yíng)模式,全靠網(wǎng)絡(luò)和口碑獲得客戶,為自己降低成本,也就是為客戶降低成本。到目前業(yè)務(wù)范圍包括了:成都網(wǎng)站制作、成都網(wǎng)站建設(shè),成都網(wǎng)站推廣,成都網(wǎng)站優(yōu)化,整體網(wǎng)絡(luò)托管,重慶小程序開(kāi)發(fā),微信開(kāi)發(fā),app軟件開(kāi)發(fā),同時(shí)也可以讓客戶的網(wǎng)站和網(wǎng)絡(luò)營(yíng)銷和我們一樣獲得訂單和生意!
# 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
持久化過(guò)程中出現(xiàn)問(wèn)題,Redis
不允許用戶進(jìn)行任何更新操作;即:stop-writes-on-bgsave-error yes
。
臨時(shí)解決方案是通過(guò)命令: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ū)е拢@需要我們對(duì)fork
原理明白,才能更好的進(jìn)行參數(shù)調(diào)整。
一般來(lái)說(shuō)Redis
在進(jìn)行RDB
的時(shí)候,會(huì)fork
出一個(gè)子進(jìn)程,子進(jìn)程和父進(jìn)程會(huì)共享一個(gè)地址空間,在fork
子進(jìn)程的時(shí)候,會(huì)檢查當(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ù)大的情況下會(huì)導(dǎo)致系統(tǒng)假死很長(zhǎng)時(shí)間,所以一般不是推薦的。
異步回寫即BGSAVE
命令,主進(jìn)程fork
后,復(fù)制自身并通過(guò)這個(gè)新的進(jìn)程回寫磁盤,回寫結(jié)束后新進(jìn)程自行關(guān)閉。由于這樣做不需要主進(jìn)程阻塞,系統(tǒng)不會(huì)假死,一般默認(rèn)會(huì)采用這個(gè)方法。
Redis
默認(rèn)采用異步回寫,所以如果我們要將數(shù)據(jù)刷到硬盤上,這時(shí)Redis
分配內(nèi)存不能太大,否則很容易發(fā)生內(nèi)存不夠用無(wú)法fork
的問(wèn)題; 設(shè)置一個(gè)合理的寫磁盤策略,否則寫頻繁的應(yīng)用,也會(huì)導(dǎo)致頻繁的fork
操作,對(duì)于占用了大內(nèi)存的Redis
來(lái)說(shuō),fork
消耗資源的代價(jià)是很大的;
Linux
對(duì)大部分申請(qǐng)內(nèi)存的請(qǐng)求都回復(fù)yes
,以便能跑更多更大的程序。
因?yàn)樯暾?qǐng)內(nèi)存后,并不會(huì)馬上使用內(nèi)存,將這些不會(huì)使用的空閑內(nèi)存分配給其它程序使用,以提高內(nèi)存利用率,這種技術(shù)叫做Overcommit
。
一般情況下,當(dāng)所有程序都不會(huì)用到自己申請(qǐng)的所有內(nèi)存時(shí),系統(tǒng)不會(huì)出問(wèn)題,但是如果程序隨著運(yùn)行,需要的內(nèi)存越來(lái)越大,在自己申請(qǐng)的大小范圍內(nèi),不斷占用更多內(nèi)存,直到超出物理內(nèi)存,當(dāng)Linux
發(fā)現(xiàn)內(nèi)存不足時(shí),會(huì)發(fā)生OOM killer(OOM=out-of-memory)
。
OOM killer
會(huì)選擇殺死一些進(jìn)程,以便釋放內(nèi)存。當(dāng)發(fā)生OOM killer
時(shí),會(huì)記錄在系統(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)用程序要申請(qǐng)的內(nèi)存 + 系統(tǒng)已經(jīng)分配的內(nèi)存)。
vm.overcommit_memory
文件指定了內(nèi)核針對(duì)內(nèi)存分配的策略,其值可以是0、1、2
。
0
:?jiǎn)l(fā)策略(默認(rèn));表示內(nèi)核將檢查是否有足夠的可用內(nèi)存供應(yīng)用進(jìn)程使用;如果有足夠的可用內(nèi)存,內(nèi)存申請(qǐng)?jiān)试S;否則,內(nèi)存申請(qǐng)失敗,并把錯(cuò)誤返回給應(yīng)用進(jìn)程。系統(tǒng)在為應(yīng)用進(jìn)程分配虛擬地址空間時(shí),會(huì)判斷當(dāng)前申請(qǐng)的虛擬地址空間大小是否超過(guò)剩余內(nèi)存大小,如果超過(guò),則虛擬地址空間分配失敗。因此,也就是如果進(jìn)程本身占用的虛擬地址空間比較大或者剩余內(nèi)存比較小時(shí),fork
、malloc
等調(diào)用可能會(huì)失敗。 0
即是啟發(fā)式的overcommitting handle
,會(huì)盡量減少swap
交換分區(qū)的使用,root
可以分配比一般用戶略多的內(nèi)存。
1
:允許overcommit
;表示內(nèi)核允許分配所有的物理內(nèi)存,而不管當(dāng)前的內(nèi)存狀態(tài)如何,允許超過(guò)CommitLimit
,這種情況下,避免了fork
可能產(chǎn)生的失敗,但由于malloc
是先分配虛擬地址空間,而后通過(guò)異常陷入內(nèi)核分配真正的物理內(nèi)存,在內(nèi)存不足的情況下,這相當(dāng)于完全屏蔽了應(yīng)用進(jìn)程對(duì)系統(tǒng)內(nèi)存狀態(tài)的感知,即malloc
總是能成功,一旦內(nèi)存不足,會(huì)引起系統(tǒng)OOM
殺進(jìn)程,應(yīng)用程序?qū)τ谶@種后果是無(wú)法預(yù)測(cè)的。 直至內(nèi)存用完為止。在數(shù)據(jù)庫(kù)服務(wù)器上不建議設(shè)置為1,從而盡量避免使用swap
交換分區(qū)。
2
:禁止overcommit
;表示不允許超過(guò)CommitLimit
值。由于很多情況下,進(jìn)程的虛擬地址空間占用遠(yuǎn)大于其實(shí)際占用的物理內(nèi)存,這樣一旦內(nèi)存使用量上去以后,對(duì)于一些動(dòng)態(tài)產(chǎn)生的進(jìn)程(需要復(fù)制父進(jìn)程地址空間)則很容易創(chuàng)建失敗,如果業(yè)務(wù)過(guò)程沒(méi)有過(guò)多的這種動(dòng)態(tài)申請(qǐng)內(nèi)存或者創(chuàng)建子進(jìn)程,則影響不大,否則會(huì)產(chǎn)生比較大的影響 。這種情況下系統(tǒng)所能分配的內(nèi)存不會(huì)超過(guò)上面提到的CommitLimit
大小,如果這么多資源已經(jīng)用光,那么后面任何嘗試申請(qǐng)內(nèi)存的行為都會(huì)返回錯(cuò)誤,這通常意味著此時(shí)沒(méi)法運(yùn)行任何新程序。
我們可以通過(guò)設(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)存空間都會(huì)小于父進(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)存頁(yè),當(dāng)父進(jìn)程處理寫請(qǐng)求的時(shí)候會(huì)把要修改的頁(yè)創(chuàng)建副本,而子進(jìn)程在fork
過(guò)程中共享整個(gè)父進(jìn)程的內(nèi)存快照。如果我們要減少創(chuàng)建的副本的大小,就涉及操作系統(tǒng)的另外一個(gè)概念Huge Pages
(大頁(yè))。
在Redhat Linux
中,內(nèi)存都是以頁(yè)的形式劃分的,默認(rèn)情況下每頁(yè)是4K
,這就意味著如果物理內(nèi)存很大,則映射表的條目將會(huì)非常多,會(huì)影響CPU
的檢索效率。因?yàn)閮?nèi)存大小是固定的,為了減少映射表的條目,可采取的辦法只有增加頁(yè)的尺寸。Linux Kernel
在2.6.38
內(nèi)核中增加了THP
(Transparent Huge Pages)的特性,支持大內(nèi)存頁(yè)(2MB
)分配,默認(rèn)開(kāi)啟。當(dāng)開(kāi)啟后可以加快fork
子進(jìn)程的速度,但fork
操作之后,每個(gè)內(nèi)存頁(yè)從原來(lái)的4KB
變成了2MB
,會(huì)大幅增加重寫期間父進(jìn)程內(nèi)存消耗,同時(shí)每次寫命令引起的復(fù)制內(nèi)存頁(yè)單位放大了512
倍,會(huì)拖慢寫操作的執(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)大頁(yè)被關(guān)閉后,可以看到同等操作下,RDB
備份時(shí)候的copy-on-write
變化內(nèi)存空間會(huì)減少。
綜上分析,我們可以操作系統(tǒng)物理內(nèi)存和Redis
內(nèi)存之間的一些關(guān)系,尤其Redis
在持久化的時(shí)候fork
進(jìn)程會(huì)隨操作系統(tǒng)的參數(shù)不同,需要的內(nèi)存也有所不同,為了加快fork
子進(jìn)程的速度以及主備之間的文件傳輸同步,一般我們建議一個(gè)Redis
節(jié)點(diǎn)的最大內(nèi)存在10G-15G
左右,操作系統(tǒng)的內(nèi)存適當(dāng)冗余,盡量控制同一臺(tái)機(jī)器的多個(gè)Redis
節(jié)點(diǎn)在同一個(gè)時(shí)間點(diǎn)進(jìn)行RDB
備份(可以通過(guò)緩存中心定時(shí)備份),導(dǎo)致內(nèi)存同一時(shí)刻增加避免內(nèi)存空間不足導(dǎo)致的fork
失敗,最安全保險(xiǎn)的情況是內(nèi)存為Redis
的2倍
,但是在vm.overcommit_memory=1和大頁(yè)關(guān)閉的情況下,可以根據(jù)實(shí)際使用,降低操作系統(tǒng)的整個(gè)內(nèi)存大小 。
參考文章:
https://www.jianshu.com/p/785ee3bea266
https://www.cnblogs.com/wjoyxt/p/3777042.html
到此,相信大家對(duì)“Redis持久化時(shí)內(nèi)存不足怎么處理”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!