溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Redis持久化時(shí)內(nèi)存不足怎么處理

發(fā)布時(shí)間:2021-06-29 14:39:28 來源:億速云 閱讀:1151 作者:chen 欄目:編程語言

本篇內(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數(shù)據(jù)回寫機(jī)制

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à)是很大的;

系統(tǒng)內(nèi)存分配策略

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,看到CommitLimitCommitted_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)行任何新程序。

解決方案

修改系統(tǒng)內(nèi)存分配策略

我們可以通過設(shè)置overcommit_memory=1的優(yōu)化,減少操作系統(tǒng)內(nèi)存,提高Redisfork成功率,因?yàn)?code>fork后的進(jìn)程和父進(jìn)程共享一個(gè)數(shù)據(jù)空間,持久化要新增的內(nèi)存空間都會小于父進(jìn)程已經(jīng)使用的空間,具體有三種方式修改內(nèi)核參數(shù),但要有root權(quán)限:

  1. 編輯/etc/sysctl.conf ,改vm.overcommit_memory=1,然后sysctl -p使配置文件生效;

  2. 命令:sysctl vm.overcommit_memory=1 ;

  3. 命令:echo 1 > /proc/sys/vm/overcommit_memory;

關(guān)閉THP(Transparent Huge Pages)

當(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 Kernel2.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)存為Redis2倍,但是在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í)!

向AI問一下細(xì)節(jié)

免責(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)容。

AI