除了 RDB 持久化之外许昨,Redis 還提供了 AOF(Append Only File)持久化功能。與 RDB 持久化通過保存數(shù)據(jù)庫(kù)中鍵值對(duì)來保存數(shù)據(jù)庫(kù)的狀態(tài)不同,AOF 持久化是通過保存 Redis 服務(wù)器所執(zhí)行的寫命令來記錄數(shù)據(jù)庫(kù)的狀態(tài)
AOF 持久化的實(shí)現(xiàn)
AOF 持久化功能的實(shí)現(xiàn)可以分為:命令追加(append),文件寫入(write),文件同步(sync)三個(gè)步驟
命令追加
AOF 持久化需要將所有寫命令記錄在文件中來保存服務(wù)器狀態(tài)届囚,而文件寫入操作效率比較低,如果每執(zhí)行一條寫命令都要寫一次 AOF 文件無(wú)疑是低效的是尖。為了提高效率意系,Redis 提供了一個(gè)中間層 – AOF 緩沖區(qū),也就是說當(dāng) Redis 執(zhí)行一條寫命令后析砸,先將該命令追加到 AOF 緩沖區(qū)中昔字,在以后的某個(gè)時(shí)刻再將 AOF 緩沖區(qū)中的內(nèi)容同步到文件中
當(dāng) AOF 持久化功能處于打開狀態(tài)時(shí),服務(wù)器在執(zhí)行完一個(gè)寫命令之后首繁,會(huì)以協(xié)議格式將被執(zhí)行的寫命令追加到服務(wù)器狀態(tài)的 aof_buf 緩沖區(qū)的末尾
AOF 文件的寫入與同步
Redis 的服務(wù)器進(jìn)程就是一個(gè)事件循環(huán)(loop)作郭,這個(gè)循環(huán)中的文件事件負(fù)責(zé)接收客戶端的命令請(qǐng)求,以及向客戶端發(fā)送命令回復(fù)弦疮,而時(shí)間事件則負(fù)責(zé)執(zhí)行像 serverCron 函數(shù)這樣需要定時(shí)運(yùn)行的函數(shù)
因?yàn)榉?wù)器在處理文件事件時(shí)可能會(huì)執(zhí)行寫命令夹攒,使得一些內(nèi)容被追加到 aof_buf 緩沖區(qū)里面,所以在服務(wù)器每次結(jié)束一個(gè)事件循環(huán)之前胁塞,它都會(huì)調(diào)用 flushAppendOnlyFile 函數(shù)咏尝,考慮是否需要將 aof_buf 緩沖區(qū)中的內(nèi)容寫入和保存到 AOF 文件里面:
def eventLoop():
while True:
# 處理文件事件,接收命令請(qǐng)求以及發(fā)送命令回復(fù)
# 處理命令請(qǐng)求時(shí)可能會(huì)有新內(nèi)容被追加到 aof_buf 緩沖區(qū)中
processFileEvents()
# 處理時(shí)間事件
processTimeEvents()
# 考慮是否要將 aof_buf 中的內(nèi)容寫入和保存到 AOF 文件里面
flushAppendOnlyFile()
flushAppendOnlyFile 函數(shù)的行為由服務(wù)器配置的 appendfsync 選項(xiàng)的值來決定:
AOF_FSYNC_NO
在該模式下啸罢,Redis 服務(wù)器在每個(gè)事件循環(huán)都將 AOF 緩沖區(qū)中的數(shù)據(jù)寫入 AOF 文件中,但不執(zhí)行同步 fsync 方法扰才,由操作系統(tǒng)決定何時(shí)同步。該模式速度最快(無(wú)需執(zhí)行同步操作)但也最不安全(如果機(jī)器崩潰將丟失上次同步后的所有數(shù)據(jù))AOF_FSYNC_ALWAYS
在該模式下衩匣,Redis 服務(wù)器在每個(gè)事件循環(huán)都將 AOF 緩沖區(qū)中的數(shù)據(jù)寫入 AOF 文件中,且執(zhí)行一次 AOF 文件同步操作琅捏。該模式速度最慢(每個(gè)事件循環(huán)都要執(zhí)行同步操作)但也最安全(如果機(jī)器崩潰只丟失當(dāng)前事件循環(huán)中處理的新數(shù)據(jù))AOF_FSYNC_EVERYSEC
在該模式下生百,Redis 服務(wù)器在每個(gè)事件循環(huán)都將 AOF 緩沖區(qū)中的數(shù)據(jù)寫入 AOF 文件中,且每秒執(zhí)行一次 AOF 文件同步操作柄延。該模式效率和安全性(如果機(jī)器崩潰只丟失前一秒處理的新數(shù)據(jù))比較適中,是 Redis 的默認(rèn)同步策略
AOF 文件的載入與數(shù)據(jù)還原
因?yàn)?Redis 的命令只能在客戶端上下文中執(zhí)行蜡坊,而載入 AOF 文件時(shí)所使用的命令直接來源于 AOF 文件而不是網(wǎng)絡(luò)連接杠输,所以服務(wù)器使用了一個(gè)沒有網(wǎng)絡(luò)連接的偽客戶端來執(zhí)行 AOF 文件保存的寫命令赎败,偽客戶端執(zhí)行命令的效果和帶網(wǎng)絡(luò)連接的客戶端執(zhí)行命令的效果完全一樣
AOF 重寫
AOF 持久化是通過保存被執(zhí)行的寫命令來記錄數(shù)據(jù)庫(kù)狀態(tài)的秕衙,所以 AOF 文件的大小隨著時(shí)間的流逝一定會(huì)越來越大;影響包括但不限于:對(duì)于 Redis 服務(wù)器据忘,計(jì)算機(jī)的存儲(chǔ)壓力;AOF 還原出數(shù)據(jù)庫(kù)狀態(tài)的時(shí)間增加
為了解決 AOF 文件體積膨脹的問題勇吊,Redis 提供了 AOF 重寫功能:Redis 服務(wù)器可以創(chuàng)建一個(gè)新的 AOF 文件來替代現(xiàn)有的 AOF 文件窍仰,新舊兩個(gè)文件所保存的數(shù)據(jù)庫(kù)狀態(tài)是相同的,但是新的 AOF 文件不會(huì)包含任何浪費(fèi)空間的冗余命令驹吮,通常體積會(huì)較舊 AOF 文件小很多
AOF 文件重寫的實(shí)現(xiàn)
AOF 文件重寫并不需要對(duì)現(xiàn)有的 AOF 文件進(jìn)行任何讀取、分析或?qū)懭氩僮鞯@個(gè)功能時(shí)通過讀取服務(wù)器當(dāng)前的數(shù)據(jù)狀態(tài)來實(shí)現(xiàn)的
實(shí)現(xiàn)原理:遍歷數(shù)據(jù)庫(kù)中的所有鍵(忽略已過期的鍵),讀取鍵現(xiàn)在的值族沃,用一條命令去記錄鍵值對(duì),代替之前修改該鍵值對(duì)的多個(gè)命令
在實(shí)際中常空,為了避免執(zhí)行命令時(shí)造成客戶端輸入緩沖區(qū)溢出,重寫程序在處理列表漓糙、哈希咐柜、集合兼蜈、有序集合這四種可能會(huì)帶有多個(gè)元素的鍵時(shí)拙友,會(huì)先檢查鍵所包含的元素?cái)?shù)量,如果超過某個(gè)限制遗契,會(huì)使用多條命令來記錄鍵的值,而不單單使用一條命令
AOF 后臺(tái)重寫
aof_rewrite 函數(shù)可以創(chuàng)建新的 AOF 文件漾根,但是這個(gè)函數(shù)會(huì)進(jìn)行大量的寫入操作,所以調(diào)用這個(gè)函數(shù)的線程將被長(zhǎng)時(shí)間的阻塞辐怕,因?yàn)?Redis 服務(wù)器使用單線程來處理命令請(qǐng)求逼蒙;所以如果直接是服務(wù)器進(jìn)程調(diào)用 aof_rewrite 函數(shù)的話,那么重寫 AOF 期間寄疏,服務(wù)器將無(wú)法處理客戶端發(fā)送來的命令請(qǐng)求
Redis 不希望 AOF 重寫會(huì)造成服務(wù)器無(wú)法處理請(qǐng)求是牢,所以 Redis 決定將 AOF 重寫程序放到子進(jìn)程(后臺(tái))里執(zhí)行。這樣處理的最大好處是:
- 子進(jìn)程進(jìn)行 AOF 重寫期間陕截,主進(jìn)程可以繼續(xù)處理命令請(qǐng)求驳棱;
- 子進(jìn)程帶有主進(jìn)程的數(shù)據(jù)副本,使用子進(jìn)程而不是線程社搅,可以避免在鎖的情況下乳规,保證數(shù)據(jù)的安全性
使用子進(jìn)程進(jìn)行 AOF 重寫的問題
子進(jìn)程在進(jìn)行 AOF 重寫期間,服務(wù)器進(jìn)程還要繼續(xù)處理命令請(qǐng)求驯妄,而新的命令可能對(duì)現(xiàn)有的數(shù)據(jù)進(jìn)行修改,這會(huì)讓當(dāng)前數(shù)據(jù)庫(kù)的數(shù)據(jù)和重寫后的 AOF 文件中的數(shù)據(jù)不一致
如何修正
為了解決這種數(shù)據(jù)不一致的問題源织,Redis 增加了一個(gè)AOF 重寫緩沖區(qū)微猖,這個(gè)緩沖區(qū)在 fork 出子進(jìn)程之后開始啟用谈息,Redis 服務(wù)器主進(jìn)程在執(zhí)行完寫命令之后凛剥,會(huì)同時(shí)將這個(gè)寫命令追加到 AOF 緩沖區(qū)和 AOF 重寫緩沖區(qū)
即子進(jìn)程在執(zhí)行 AOF 重寫時(shí),主進(jìn)程需要執(zhí)行以下三個(gè)工作:
- 執(zhí)行 client 發(fā)來的命令請(qǐng)求
- 將寫命令追加到現(xiàn)有的 AOF 文件中
- 將寫命令追加到 AOF 重寫緩存中
可以保證:
- AOF 緩沖區(qū)的內(nèi)容會(huì)定期被寫入和同步到 AOF 文件中逻炊,對(duì)現(xiàn)有的 AOF 文件的處理工作會(huì)正常進(jìn)行
- 從創(chuàng)建子進(jìn)程開始犁享,服務(wù)器執(zhí)行的所有寫操作都會(huì)被記錄到 AOF 重寫緩沖區(qū)中
當(dāng)子進(jìn)程完成對(duì) AOF 文件重寫之后,它會(huì)向父進(jìn)程發(fā)送一個(gè)完成信號(hào)桨吊,父進(jìn)程接到該完成信號(hào)之后,會(huì)調(diào)用一個(gè)信號(hào)處理函數(shù)视乐,該函數(shù)完成以下工作:
- 將 AOF 重寫緩存中的內(nèi)容全部寫入到新的 AOF 文件中;這個(gè)時(shí)候新的 AOF 文件所保存的數(shù)據(jù)庫(kù)狀態(tài)和服務(wù)器當(dāng)前的數(shù)據(jù)庫(kù)狀態(tài)一致
- 對(duì)新的 AOF 文件進(jìn)行改名留美,原子地覆蓋原有的 AOF 文件渣聚;完成新舊兩個(gè) AOF 文件的替換
當(dāng)這個(gè)信號(hào)處理函數(shù)執(zhí)行完畢之后独榴,主進(jìn)程就可以繼續(xù)像往常一樣接收命令請(qǐng)求了
在整個(gè) AOF 后臺(tái)重寫過程中奕枝,只有最后的信號(hào)處理函數(shù)執(zhí)行時(shí)會(huì)造成主進(jìn)程阻塞瓶堕,在其他時(shí)候,AOF 后臺(tái)重寫都不會(huì)對(duì)主進(jìn)程造成阻塞谭梗,這將 AOF 重寫對(duì)性能造成的影響降到最低
以上宛蚓,即 AOF 后臺(tái)重寫,也就是 BGREWRITEAOF 命令的工作原理