目錄:
- RDB
- AOF
- 持久化恢復(fù)
- 問題排查和性能優(yōu)化
fork 操作
子進(jìn)程開銷
AOF 追加阻塞
單機(jī)多實(shí)例部署 - 總結(jié)
1 前言
Redis 相比較于 memcache狼电,多了持久化的功能迎献。而持久化分為 2 種镐躲,一種是 RDB(全量)滥沫,一種是 AOF(增量)对人。
RDB 是舊的模式涛舍,現(xiàn)在基本上都使用 AOF暂筝。當(dāng)然箩言,今天兩個(gè)都會(huì)一起聊聊。
2 RDB
RDB 流程圖:
RDB 特點(diǎn):
- RDB 是一種快照模式焕襟,即——保存的是 key value 數(shù)據(jù)內(nèi)容陨收。
- RDB 有 2 種持久方式,同步 save 模式和異步 bgsave 模式鸵赖。由于 save 是同步的务漩,所以可以保證數(shù)據(jù)一致性,而 bgsave 則不能它褪。
- save 可以在客戶端顯式觸發(fā)饵骨,也可以在 shutdown 時(shí)自動(dòng)觸發(fā);bgsave 可以在客戶端顯式觸發(fā)茫打,也可以通過配置由定時(shí)任務(wù)觸發(fā)居触,也可以在 slave 節(jié)點(diǎn)觸發(fā)。
- save 導(dǎo)致 redis 同步阻塞老赤,基本已經(jīng)廢棄轮洋。bgsave 則不會(huì)導(dǎo)致阻塞,但也有缺點(diǎn):在 fork 時(shí)抬旺,需要增加內(nèi)存服務(wù)器開銷弊予,因?yàn)楫?dāng)內(nèi)存不夠時(shí),將使用虛擬內(nèi)存开财,導(dǎo)致阻塞 Redis 運(yùn)行汉柒。所以,需要保證空閑內(nèi)存足夠责鳍。
- 默認(rèn)執(zhí)行 shutdown 時(shí)竭翠,如果沒有開啟 AOF,則自動(dòng)執(zhí)行 bgsave薇搁。
- 每次的 RDB 文件都是替換的。
關(guān)于優(yōu)化:Redis 會(huì)壓縮 RDB 文件渡八,使用 LZF 算法啃洋,讓最終的 RDB 文件遠(yuǎn)小于內(nèi)存大小传货,默認(rèn)開啟。但會(huì)消耗 CPU宏娄。
RDB 缺點(diǎn):
- 無法秒級(jí)持久化问裕。
- 老版本 Redis 無法兼容新版本 RDB。
RDB 優(yōu)點(diǎn):
- 文件緊湊孵坚,適合備份粮宛,全量復(fù)制場(chǎng)景。例如每 6 小時(shí)執(zhí)行 bgsave卖宠,保存到文件系統(tǒng)之類的巍杈。
- Redis 加載 RDB 恢復(fù)數(shù)據(jù)遠(yuǎn)遠(yuǎn)快于 AOF。
3 AOF
由于 RDB 的數(shù)據(jù)實(shí)時(shí)性問題扛伍,AOF(append only file) 是目前 Redis 持久化的主流方式筷畦。
AOF 特點(diǎn):
- 默認(rèn)文件名是 appendonly.aof。和 RDB 一樣刺洒,保存在配置中 dir 目錄下鳖宾。
- AOF 相比較于 RDB,每次都會(huì)保存寫命令逆航,數(shù)據(jù)實(shí)時(shí)性更高鼎文。
- AOF 由于每次都會(huì)記錄寫命令,文件會(huì)很大因俐,因此需要進(jìn)行優(yōu)化拇惋,稱之為“重寫機(jī)制”(下面詳細(xì)說)。
- AOF 每次保存的寫命令都放在一個(gè)緩沖區(qū)女揭,根據(jù)不同的策略(下面詳細(xì)說)同步到磁盤蚤假。
“重寫機(jī)制” 細(xì)節(jié):
- fork 子進(jìn)程(類似 bgsave)
- 主進(jìn)程會(huì)寫到2個(gè)緩沖區(qū),一個(gè)是原有的 “AOF 緩存區(qū)”吧兔,一個(gè)是專門為子進(jìn)程準(zhǔn)備的 “AOF 重寫緩沖區(qū)”磷仰;
- 子進(jìn)程寫到到新的 AOF 文件中,批量的境蔼,默認(rèn) 32m灶平;寫完后通知主進(jìn)程。
- 主進(jìn)程把“AOF 重寫緩沖區(qū)”的數(shù)據(jù)寫到新 AOF 文件中箍土。
- 將新的 AOF 文件替換老文件逢享。
重寫流程圖:
緩沖區(qū)同步策略,由參數(shù) appendfsync 控制吴藻,一共3種:
- always:調(diào)用系統(tǒng) fsync 函數(shù)瞒爬,直到同步到硬盤返回;
嚴(yán)重影響 redis 性能
。 - everysec:先調(diào)用 OS write 函數(shù)侧但, 寫到緩沖區(qū)矢空,然后 redis 每秒執(zhí)行一次 OS fsync 函數(shù)。
推薦使用這種方式
禀横。 - no: 只執(zhí)行 write OS 函數(shù)屁药,具體同步硬盤策略由 OS 決定;
不推薦柏锄,數(shù)據(jù)不安全酿箭,容易丟失數(shù)據(jù)
。
4 持久化恢復(fù)
AOF 和 RDB 文件都可以用于服務(wù)器重啟時(shí)的數(shù)據(jù)恢復(fù)趾娃,具體流程如下圖:
從圖中可以看出優(yōu)先加載 AOF缭嫡,當(dāng)沒有 AOF 時(shí)才加載 RDB。當(dāng) AOF 或者 RDB 存在錯(cuò)誤茫舶,則加載失敗械巡。
5 問題排查和性能優(yōu)化
Redis 持久化是影響 Redis 性能的高發(fā)地,也是面試中常問的問題饶氏。
1. fork 操作
當(dāng) Redis 做 RDB 或者 AOF 重寫時(shí)讥耗,必然要進(jìn)行 fork 操作,對(duì)于 OS 來說疹启,fork 都是一個(gè)重量級(jí)操作古程。而且,fork 還會(huì)拷貝一些數(shù)據(jù)喊崖,雖然不會(huì)拷貝主進(jìn)程所有的物理空間挣磨,但會(huì)復(fù)制主進(jìn)程的空間內(nèi)存頁表。對(duì)于 10GB 的 Redis 進(jìn)程荤懂,需要復(fù)制大約 20MB 的內(nèi)存頁表茁裙,因此 fork 操作耗時(shí)跟進(jìn)程總內(nèi)存量息息相關(guān),再加上节仿,如果使用虛擬化技術(shù)晤锥,例如 Xen 虛擬機(jī),fork 會(huì)更加耗時(shí)廊宪。
一個(gè)正常的 fork 耗時(shí)大概在 20毫秒左右矾瘾。為什么呢,假設(shè)一個(gè) Redis 實(shí)例的 OPS 在 5 萬以上箭启,如果 fork 操作耗時(shí)在秒級(jí)壕翩,那么僵拖慢幾萬條命令的執(zhí)行,對(duì)生產(chǎn)環(huán)境影響明顯傅寡。
我們可以在 Info stats 統(tǒng)計(jì)中查詢 latest_fork_usec 指標(biāo)獲取最近一次 fork 操作耗時(shí)放妈,單位微秒北救。
如何優(yōu)化:
- 優(yōu)先使用物理機(jī)或者高效支持 fork 的虛擬化技術(shù),避免使用 Xen大猛。
- 控制 redis 實(shí)例最大內(nèi)存扭倾,盡量控制在 10GB 以內(nèi)。
- 合理配置 Linux 內(nèi)存分配策略挽绩,避免內(nèi)存不足導(dǎo)致 fork 失敗。
- 降低 fork 的頻率驾中,如適度放寬 AOF 自動(dòng)觸發(fā)時(shí)機(jī)唉堪,避免不必要的全量復(fù)制。
2. 子進(jìn)程開銷
fork 完畢之后肩民,會(huì)創(chuàng)建子進(jìn)程唠亚,子進(jìn)程負(fù)責(zé) RDB 或者 AOF 重寫,這部分過程主要涉及到 CPU持痰,內(nèi)存灶搜,硬盤三個(gè)地方的優(yōu)化。
CPU
寫入文件的過程是 CPU 密集的過程工窍,通常子進(jìn)程對(duì)單核 CPU 利用率接近 90%割卖。
如何優(yōu)化呢?既然是 CPU 密集型操作患雏,就不要綁定單核 CPU鹏溯,因?yàn)檫@樣會(huì)和父 CPU 進(jìn)行競(jìng)爭(zhēng)。同時(shí)淹仑,不要和其他 CPU 密集型服務(wù)不是在一個(gè)機(jī)器上丙挽。如果部署了多個(gè) Redis 實(shí)例,盡力保證統(tǒng)一時(shí)刻只有一個(gè)子進(jìn)程執(zhí)行重寫工作匀借。內(nèi)存
子進(jìn)程通過 fork 操作產(chǎn)生颜阐,占用內(nèi)存大小等同于父進(jìn)程,理論上需要兩倍的內(nèi)存完成持久化操作吓肋,但 Linux 有 copy on write 機(jī)制凳怨,父子進(jìn)程會(huì)共享相同的物理內(nèi)存頁,當(dāng)父進(jìn)程處理寫操作時(shí)蓬坡,會(huì)把要修改的頁創(chuàng)建對(duì)應(yīng)的副本猿棉,而子進(jìn)程在 fork 操作過程中,共享整個(gè)父進(jìn)程內(nèi)存快照屑咳。
即——如果重寫過程中存在內(nèi)存修改操作萨赁,父進(jìn)程負(fù)責(zé)創(chuàng)建所修改內(nèi)存頁的副本。這里就是內(nèi)存消耗的地方兆龙。
如何優(yōu)化呢杖爽?盡量保證同一時(shí)刻只有一個(gè)子進(jìn)程在工作敲董;避免大量寫入時(shí)做重寫操作。硬盤
硬盤開銷分析:子進(jìn)程主要職責(zé)是將 RDB 或者 AOF 文件寫入硬盤進(jìn)行持久化慰安,勢(shì)必對(duì)硬盤造成壓力腋寨,可通過工具例如 iostat,iotop 等化焕,分析硬盤負(fù)載情況萄窜。
如何優(yōu)化:
- 不要和其他高硬盤負(fù)載的服務(wù)放在一臺(tái)機(jī)器上,例如 MQ撒桨,存儲(chǔ)查刻。
- AOF 重寫時(shí)會(huì)消耗大量硬盤 IO,可以開啟配置 no-appendfsync-on-rewrite凤类,默認(rèn)關(guān)閉穗泵。表示在 AOF 重寫期間不做 fsync 操作。
- 當(dāng)開啟 AOF 的 Redis 在高并發(fā)場(chǎng)景下谜疤,如果使用普通機(jī)械硬盤佃延,每秒的寫速率是 100MB左右,這時(shí)夷磕,Redis 的性能瓶頸在硬盤上履肃,建議使用 SSD。
- 對(duì)于單機(jī)配置多個(gè) Redis 實(shí)例的情況企锌,可以配置不同實(shí)例分盤存儲(chǔ) AOF 文件榆浓,分?jǐn)傆脖P壓力。
3. AOF 追加阻塞
當(dāng)開啟 AOF 持久化時(shí)撕攒,常用的同步硬盤的策略是“每秒同步” everysec陡鹃,用于平衡性能和數(shù)據(jù)安全性,對(duì)于這種方式抖坪,redis 使用另一條線程每秒執(zhí)行 fsync 同步硬盤萍鲸,當(dāng)系統(tǒng)資源繁忙時(shí),將造成 Redis 主線程阻塞擦俐。
流程圖如下:
通過上圖可以發(fā)現(xiàn):everysec 配置最多可能丟失 2 秒數(shù)據(jù)脊阴,不是 1 秒;如果系統(tǒng) fsync 緩慢蚯瞧,將會(huì)導(dǎo)致 Redis 主線程阻塞影響效率嘿期。
問題定位:
- 發(fā)生 AOF 阻塞時(shí),會(huì)輸入日志埋合。用于記錄 AOF fsync 阻塞導(dǎo)致拖慢 Redis 服務(wù)的行為备徐。
- 每當(dāng) AOF 追加阻塞事件發(fā)生時(shí),在 info Persistence 統(tǒng)計(jì)中甚颂,aof_delayed_fsync 指標(biāo)會(huì)累加蜜猾,查看這個(gè)指標(biāo)方便定位 AOF 阻塞問題秀菱。
- AOF 同步最多運(yùn)行 2 秒的延遲,當(dāng)延遲發(fā)生時(shí)說明硬盤存在性能問題蹭睡,可通過監(jiān)控工具 iotop 查看衍菱,定位消耗 IO 的進(jìn)程。
4. 單機(jī)多實(shí)例部署
Redis 單線程架構(gòu)無法充分利用多核CPU肩豁,通常的做法是一臺(tái)機(jī)器上部署多個(gè)實(shí)例脊串,當(dāng)多個(gè)實(shí)例開啟 AOF 后,彼此之間就會(huì)產(chǎn)生CPU 和 IO 的競(jìng)爭(zhēng)清钥。
如何解決這個(gè)問題呢洪规?
讓所有實(shí)例的 AOF 串行執(zhí)行。
我們通過 info Persistence 中關(guān)于 AOF 的信息寫出 Shell 腳本循捺,然后串行執(zhí)行實(shí)例的 AOF 持久化。
整個(gè)過程如圖:
通過不斷判斷 AOF 的狀態(tài)雄人,手動(dòng)執(zhí)行 AOF 重寫从橘,保證 AOF 不會(huì)存在競(jìng)爭(zhēng)。具體的 Shell 編寫以及 info 信息判斷础钠,可以查看下圖:
6 總結(jié)
本文主要講了 Redis 的持久化相關(guān)功能恰力,持久化一直是影響 Redis 性能的高發(fā)地,也是面試中經(jīng)常被問到的旗吁。包括 RDB 相關(guān)的特定和優(yōu)缺點(diǎn)踩萎,AOF 的優(yōu)缺點(diǎn),事實(shí)上很钓,由于 RDB 的數(shù)據(jù)實(shí)時(shí)性問題香府,目前用 AOF 比較多了。而持久化恢復(fù)也是優(yōu)先 AOF码倦。
關(guān)于持久化的問題排查企孩,就很麻煩了,但無非幾個(gè)方面袁稽,fork 耗時(shí)勿璃,子進(jìn)程的 CPU,內(nèi)存推汽,硬盤開銷补疑,AOF 的同步阻塞,單機(jī)多實(shí)例部署歹撒。
這些優(yōu)化莲组,可以通過前面寫的分析進(jìn)行排查。
引用
《Redis 開發(fā)與運(yùn)維》
《深入分布式緩存》