? ? ? ?在最近的工作中,碰到很多Redis的問題贬丛,項目組將Redis當做黑盒子使用撩银,這是一件很危險的事情,導致很多意想不到的事情發(fā)生豺憔,在使用Redis的時候额获,我們必須理解redis.conf中常用參數(shù)的意義,例如maxmemory最大內(nèi)存恭应,默認是操作系統(tǒng)可用的最大內(nèi)存抄邀,這個明顯在生產(chǎn)上是必須配置具體值,但是很遺憾暮屡,項目組在使用中沒有調(diào)整撤摸。今天我要講的是redis的內(nèi)存使用,redis的內(nèi)存其實有數(shù)據(jù)內(nèi)存褒纲,進程內(nèi)存准夷,輸入緩存區(qū)大小,輸出連接動態(tài)內(nèi)存等不同的概念莺掠,這里我們只針對RDB和AOF持久化的時候衫嵌,fork進程需要的內(nèi)存大小進行講解,從而理解一臺虛擬機內(nèi)存和Redis內(nèi)存的配置關系彻秆。
1楔绞、fork 的原理
? ? ? ? 在redis在進行持久化的時候,有的時候可以在日志中看到fork進程失敗的提示唇兑,一般是系統(tǒng)可用的內(nèi)存空間不夠導致酒朵,這需要我們對fork原理明白,才能更好的進行參數(shù)調(diào)整扎附。一般來說Redis在進行RDB的時候蔫耽,會fork出一個子進程,子進程和父進程會共享一個地址空間留夜,在fork子進程的時候匙铡,會檢查當前機器可用的內(nèi)存是否滿足fork出一個子進程的要求图甜,一般由操作系統(tǒng)?overcommit_memory決定。
overcommit_memory參數(shù)說明: 設置內(nèi)存分配策略(可選鳖眼,根據(jù)服務器的實際情況進行設置) /proc/sys/vm/overcommit_memory 可選值:
?0黑毅, 表示內(nèi)核將檢查是否有足夠的可用內(nèi)存供應用進程使用;如果有足夠的可用內(nèi)存钦讳,內(nèi)存申請允許矿瘦;否則,內(nèi)存申請失敗蜂厅,并把錯誤返回給應用進程匪凡。 操作系統(tǒng)默認參數(shù),如果redis進程使用了9G空間掘猿,fork需要9G內(nèi)存,但是整個操作系統(tǒng)內(nèi)存16G唇跨,9+9>16稠通,那么fork會失敗。
1买猖, 表示內(nèi)核允許分配所有的物理內(nèi)存改橘,而不管當前的內(nèi)存狀態(tài)如何,實際在fork出子進程真正執(zhí)行的時候玉控,需要的內(nèi)存并不會有父進程那么大飞主,如果redis進程使用了9G,整個內(nèi)存16G高诺,這個時候只要有剩余內(nèi)存碌识,就會fork成功,而且子進程在RDB的時候虱而,整個需要的內(nèi)存空間并不需要9G筏餐,因為父子進程共享數(shù)據(jù)空間,需要新增的的內(nèi)存空間是RDB備份過程中 copy-on-writer變化的數(shù)據(jù)空間牡拇,取決于數(shù)據(jù)變化和頁空間魁瞪,一般都不會特別大。
2惠呼, 表示內(nèi)核允許分配超過所有物理內(nèi)存和交換空間總和的內(nèi)存导俘,根據(jù)vm.overcommit_ratio定義的值,允許分配超出物理內(nèi)存加上交換內(nèi)存的請求剔蹋。vm.overcommit_ratio參數(shù)是一個百分比旅薄,加上內(nèi)存量決定內(nèi)存可以超量分配多少內(nèi)存。例如滩租,vm.overcommit_ratio值為50赋秀,而內(nèi)存有16GB利朵,那么這意味著在內(nèi)存分配請求失敗前,加上交換內(nèi)存4G猎莲,內(nèi)存將允許高達16*.0.5+4=12GB的內(nèi)存分配請求
? ? ? 通過上面的分析绍弟,我們可以看到,針對Redis的持久化著洼,尤其是在一臺機器上部署多個節(jié)點的時候樟遣,我們可以通過設置overcommit_memory=1的優(yōu)化,減少操作系統(tǒng)內(nèi)存身笤,提高redis的fork成功率豹悬,因為fork后的進程和父進程共享一個數(shù)據(jù)空間,持久化要新增的內(nèi)存空間都會小于父進程已經(jīng)使用的空間液荸,具體設置為:
echo “vm.overcommit_memory=1” >> /etc/sysctl.conf
sysctl vm.overcommit_memory=1
2瞻佛、減少fork時父進程變化內(nèi)存
? ? ? 上面講到,當redis持久化fork子進程后娇钱,占用內(nèi)存大小和父進程等同伤柄,由于Linux在寫時有copy-on-write機制,父子進程共享相同的物理內(nèi)存頁文搂,當父進程處理寫請求的時候會把要修改的頁創(chuàng)建副本适刀,而子進程在fork過程中共享整個父進程的內(nèi)存快照,在RDB重寫時煤蹭,Redis的日志輸出 RDB: 657 MB of memory used by copy-on-write笔喉。代表重寫過程中有內(nèi)存頁被修改,父進程創(chuàng)建了副本硝皂,這部分的內(nèi)存空間是657MB的大小常挚。如果我們要減少創(chuàng)建的副本的大小,就涉及操作系統(tǒng)的另外一個概念 Huge Pages 大頁吧彪。
? ? ? ?在Redhat Linux中待侵,內(nèi)存都是以頁的形式劃分的,默認情況下每頁是4K姨裸,這就意味著如果物理內(nèi)存很大秧倾,則映射表的條目將會非常多,會影響CPU的檢索效率傀缩。因為內(nèi)存大小是固定的那先,為了減少映射表的條目,可采取的辦法只有增加頁的尺寸赡艰。Linux Kernel在2.6.38內(nèi)核中增加了THP的特性售淡,支持大內(nèi)存頁(2MB)分配,默認開啟。當開啟后可以加快fork子進程的速度揖闸,但fork操作之后揍堕,每個內(nèi)存頁從原來的4KB變成了2MB,會大幅增加重寫期間父進程內(nèi)存消耗汤纸,同時每次寫命令引起的復制內(nèi)存頁單位放大了512倍衩茸,會拖慢寫操作的執(zhí)行時間,因此在Redis的時候日志建議是對此特性進行禁用贮泞,方法為: echo never > /sys/kernel/mm/transparent_hugepage/enabled楞慈。為了讓機器重啟該參數(shù)仍然生效,建議在/etc/rc.local中追加啃擦,避免失效囊蓝。當大頁被關閉后,可以看到同等操作下令蛉,RDB備份時候的copy-on-write變化內(nèi)存空間會減少聚霜。
? ? ?綜上分析,我們可以操作系統(tǒng)物理內(nèi)存和Redis內(nèi)存之間的一些關系珠叔,尤其Redis在持久化的時候fork進程會隨操作系統(tǒng)的參數(shù)不同俯萎,需要的內(nèi)存也有所不同,為了加快fork子進程的速度以及主備之間的文件傳輸同步运杭,一般我們建議一個Redis節(jié)點的最大內(nèi)存在10G-15G左右,操作系統(tǒng)的內(nèi)存適當冗余函卒,盡量控制同一臺機器的多個Redis節(jié)點在同一個時間點進行RDB備份(可以通過緩存中心定時備份)辆憔,導致內(nèi)存同一時刻增加避免內(nèi)存空間不足導致的fork失敗,最安全保險的情況是內(nèi)存為Redis的2倍报嵌,但是在vm.overcommit_memory=1和大頁管關閉的情況下虱咧,可以根據(jù)實際使用,降低操作系統(tǒng)的整個內(nèi)存大小 锚国。
? ? ? 對于使用人員來說腕巡,一定要清楚原理,重點評估Redis的總節(jié)點數(shù)和操作系統(tǒng)內(nèi)存的關系血筑,避免持久化和高可用性帶來的內(nèi)存不足問題绘沉。