RDB持久化通過(guò)保存數(shù)據(jù)庫(kù)中的鍵值對(duì)來(lái)記錄數(shù)據(jù)庫(kù)狀態(tài)
AOF持久化是通過(guò)保存Redis服務(wù)器所執(zhí)行的寫命令來(lái)記錄數(shù)據(jù)庫(kù)狀態(tài)。

被寫入AOF文件的所有命令都是以Redis的命令請(qǐng)求協(xié)議格式保存的,因?yàn)镽edis的命令請(qǐng)求協(xié)議是純文本格式,所以可以直接打開(kāi)一個(gè)AOF文件,觀察里面的內(nèi)容。
服務(wù)器在啟動(dòng)時(shí),可以通過(guò)載入和執(zhí)行AOF文件中保存的命令來(lái)還原服務(wù)器關(guān)閉之前的狀態(tài)。
11.1 AOF持久化的實(shí)現(xiàn)
AOF的持久化功能的實(shí)現(xiàn)可以分為命令追加、文件寫入、文件同步三個(gè)部分。
11.1.1 命令追加
當(dāng)服務(wù)器打開(kāi)AOF持久化功能時(shí),服務(wù)器在執(zhí)行完一個(gè)寫命令之后,會(huì)以協(xié)議格式將被執(zhí)行的寫命令追加到服務(wù)器狀態(tài)的aof_buf緩沖區(qū)的末尾
11.1.2 AOF文件的寫入和同步
Redis服務(wù)器進(jìn)程就是一個(gè)事件循環(huán),這個(gè)循環(huán)中的文件事件負(fù)責(zé)接受客戶端的命令請(qǐng)求,以及向客戶端發(fā)送命令回復(fù),而時(shí)間事件則負(fù)責(zé)執(zhí)行像serverCron函數(shù)這樣需要定時(shí)運(yùn)行的函數(shù)。
服務(wù)器在每次結(jié)束一個(gè)事件循環(huán)之前,都會(huì)調(diào)用flushAppendOnlyFile函數(shù),考慮是否需要將aof_buf緩沖區(qū)中的內(nèi)容寫入和保存到AOF文件里面。
文件的寫入和同步
為了提高文件的寫入效率,現(xiàn)代操作系統(tǒng)中,當(dāng)用戶調(diào)用write函數(shù),將一些數(shù)據(jù)寫入文件時(shí),操作系統(tǒng)通常會(huì)將數(shù)據(jù)先暫時(shí)保存在一個(gè)內(nèi)存緩沖區(qū)里面,等到緩沖區(qū)被填滿時(shí)或者超過(guò)了指定的時(shí)限之后,才真正地將緩沖區(qū)中的數(shù)據(jù)寫入磁盤中。
這種做法雖然提高了效率,但是也為寫入數(shù)據(jù)帶來(lái)了安全問(wèn)題,為此系統(tǒng)提供了fsync和fdatasync兩個(gè)同步函數(shù),他們可以強(qiáng)制讓操作系統(tǒng)立即將緩沖區(qū)中的數(shù)據(jù)寫入到硬盤里面,從而確保寫入數(shù)據(jù)的安全性。
AOF持久化的效率和安全性
服務(wù)器配置appendfsync選項(xiàng)的值直接決定AOF持久化的效率和安全性。
appendfsync的值可以為no、everysec和always
當(dāng)appendfsync的值為always時(shí),服務(wù)器在每個(gè)事件循環(huán)都要將aof_buf緩沖區(qū)中的所有內(nèi)容寫入到AOF文件,并且同步AOF文件,所以always到效率是appendfsync三個(gè)值中最慢的,但是安全性是三個(gè)值中最高的,及時(shí)出現(xiàn)故障停機(jī),AOF持久化也只會(huì)丟失一個(gè)事件循環(huán)中所產(chǎn)生的命令數(shù)據(jù)。
當(dāng)appendfsync的值為everysec時(shí),服務(wù)器在每個(gè)事件循環(huán)都要把a(bǔ)of_buf緩沖區(qū)的所有內(nèi)容寫入到AOF文件中,每隔1s就要在子進(jìn)程中對(duì)AOF文件進(jìn)行一次同步。從效率上講,everysec模式足夠快,并且就算出現(xiàn)故障停機(jī),數(shù)據(jù)庫(kù)也只丟失1s的命令數(shù)據(jù)。
當(dāng)appendfsync的值為no時(shí),服務(wù)器在每個(gè)事件循環(huán)都要把a(bǔ)of_buf緩沖區(qū)的所有內(nèi)容寫入到AOF文件中,至于何時(shí)對(duì)AOF文件執(zhí)行同步,由操作系統(tǒng)控制。因?yàn)樘幱趎o模式下的flushappendOnlyFile調(diào)用無(wú)需執(zhí)行同步操作,所以該模式下的AOF文件寫入速度是最快的,因?yàn)檫@種模式會(huì)在系統(tǒng)緩存中累積一段時(shí)間的寫入數(shù)據(jù),所以這個(gè)模式的單次同步時(shí)間是3種模式中最長(zhǎng)的。從平攤操作的角度來(lái)看,no模式和everysec模式的效率類型,當(dāng)出現(xiàn)故障停機(jī)時(shí),使用no模式的服務(wù)器將丟失上次同步AOF文件之后的所有寫命令數(shù)據(jù)。
11.2 AOF文件的載入和還原
AOF文件中保存了重建數(shù)據(jù)庫(kù)狀態(tài)的所有寫命令,所以服務(wù)器只需讀入并重新執(zhí)行一遍AOF文件里保存的寫命令,就可以還原服務(wù)器關(guān)閉之前的數(shù)據(jù)庫(kù)狀態(tài)。
還原數(shù)據(jù)庫(kù)的詳細(xì)步驟:
1.創(chuàng)建一個(gè)不帶網(wǎng)絡(luò)連接的偽客戶端:因?yàn)镽edis的命令只能在客戶端上下文運(yùn)行,而載入AOF文件時(shí)所使用的命令直接來(lái)源于AOF文件而不是網(wǎng)絡(luò)連接,所以服務(wù)器使用了一個(gè)沒(méi)有網(wǎng)絡(luò)連接的偽客戶端來(lái)執(zhí)行AOF文件中保存的命令,偽客戶端執(zhí)行命令的效果和帶網(wǎng)絡(luò)連接的客戶端執(zhí)行命令的效果是一致的。
2.從AOF文件中分析并讀取出一條寫命令。
3.使用偽客戶端執(zhí)行被讀出的寫命令
4.一直執(zhí)行2.3,直到AOF中的所有寫命令都被處理完畢為止。
11.3 AOF重寫
因?yàn)锳OF持久化是通過(guò)保存被執(zhí)行的寫命令來(lái)記錄數(shù)據(jù)庫(kù)的狀態(tài)的,所以隨著服務(wù)器運(yùn)行時(shí)間的流逝,AOF文件中的內(nèi)容會(huì)越來(lái)越多,文件的體積也會(huì)越來(lái)越大,如果不加以控制的話,體積過(guò)大的AOF文件很可能對(duì)Redis服務(wù)器、甚至整個(gè)宿主計(jì)算機(jī)造成影響,并且AOF文件的體積越大,使用AOF文件進(jìn)行數(shù)據(jù)還原所需的時(shí)間就越多。
11.3.1 AOF文件重寫的實(shí)現(xiàn)
Redis將新生成的AOF文件替換舊的AOF文件的功能命名為“AOF文件重寫”,這個(gè)功能是通過(guò)讀取服務(wù)區(qū)當(dāng)前的數(shù)據(jù)庫(kù)狀態(tài)來(lái)實(shí)現(xiàn)的。
AOF重寫功能的實(shí)現(xiàn)原理:首先從數(shù)據(jù)庫(kù)中讀取鍵現(xiàn)在的值,然后用一條命了去記錄鍵值對(duì),代替之前記錄這個(gè)鍵值對(duì)的多條命令。
注意:為了避免在執(zhí)行命令時(shí)造成客戶端輸入緩沖區(qū)溢出,重寫程序在處理列表、哈希表、集合、有序集合這四種可能會(huì)帶有多個(gè)元素的鍵時(shí),會(huì)先檢查鍵所包含的元素?cái)?shù)量,如果元素?cái)?shù)量超過(guò)了redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD常量的值,那么重寫程序?qū)⑹褂枚鄺l命令來(lái)記錄鍵的值,目前版本REDIS_AOF_REWIRTE_ITEMS_PER_CMD常量的值為64.
11.3.2 AOF后臺(tái)重寫
aof_rewrite函數(shù)可以很好地完成創(chuàng)建一個(gè)新AOF文件的任務(wù) ,但是,因?yàn)檫@個(gè)函數(shù)會(huì)進(jìn)行大量的寫入操作,所以調(diào)用這個(gè)函數(shù)的線程將被長(zhǎng)時(shí)間阻塞,因?yàn)镽edis是單進(jìn)程單線程來(lái)處理命令請(qǐng)求,所以如果由服務(wù)器直接調(diào)用aof_write函數(shù)的話,在重寫AOF文件期間,服務(wù)器將無(wú)法處理客戶端發(fā)來(lái)的命令請(qǐng)求。
Redis決定將AOF重寫放入子進(jìn)程中執(zhí)行,這樣做可以同時(shí)達(dá)到兩個(gè)目的:
1.子進(jìn)程進(jìn)行AOF重寫期間,服務(wù)器進(jìn)程(父進(jìn)程)可以繼續(xù)處理命令請(qǐng)求
2.子進(jìn)程帶有服務(wù)器進(jìn)程的數(shù)據(jù)副本,使用子進(jìn)程而不是線程,就是為了在避免使用鎖的情況下,保證數(shù)據(jù)的安全性。
一個(gè)問(wèn)題需要解決:因?yàn)樽舆M(jìn)程在進(jìn)行AOF重寫期間,服務(wù)器進(jìn)程還需要繼續(xù)處理命令請(qǐng)求,而新的命令可能會(huì)對(duì)現(xiàn)有的數(shù)據(jù)庫(kù)狀態(tài)進(jìn)行修改,從而使得服務(wù)器當(dāng)前的數(shù)據(jù)庫(kù)狀態(tài)和重寫后的AOF文件所保存的數(shù)據(jù)庫(kù)狀態(tài)不一致。
解決方法:redis服務(wù)器設(shè)置了一個(gè)AOF重寫緩沖區(qū),這個(gè)緩沖區(qū)在服務(wù)器創(chuàng)建子進(jìn)程之后開(kāi)始使用,當(dāng)Redis服務(wù)器執(zhí)行完一個(gè)寫命令之后,它會(huì)同時(shí)將這個(gè)寫命令發(fā)送給AOF緩沖區(qū)和AOF重寫緩沖區(qū)。
在子進(jìn)程執(zhí)行AOF重寫期間,服務(wù)器需要進(jìn)行以下操作:
1.執(zhí)行客戶端發(fā)來(lái)的命令。
2.將執(zhí)行后的寫命令追加到AOF緩沖區(qū)。
3.將執(zhí)行后的寫命令追加到AOF重寫緩沖區(qū)。
可以保證:
AOF緩沖區(qū)的內(nèi)容會(huì)定期被寫入和同步到AOF文件中,對(duì)現(xiàn)有AOF文件的處理會(huì)如常進(jìn)行。
從創(chuàng)建子進(jìn)程開(kāi)始,服務(wù)器執(zhí)行的所有寫命令都會(huì)被記錄到AOF重寫緩沖區(qū)里面。
當(dāng)子進(jìn)程完成AOF重寫工作之后,它會(huì)向父進(jìn)程發(fā)送一個(gè)信號(hào),父進(jìn)程在接到該信號(hào)之后,會(huì)調(diào)用一個(gè)信號(hào)處理函數(shù),并執(zhí)行以下操作:
1)將AOF重寫緩沖區(qū)的所有內(nèi)容寫入到新的AOF文件中,這時(shí),新的AOF文件保存的內(nèi)容和當(dāng)前數(shù)據(jù)庫(kù)狀態(tài)一致。
2)對(duì)新的AOF文件進(jìn)行改名,原子地覆蓋現(xiàn)有的AOF文件,完成新舊兩個(gè)AOF文件的替換。
整個(gè)AOF后臺(tái)重寫過(guò)程中,只有信號(hào)處理函數(shù)執(zhí)行會(huì)對(duì)服務(wù)器進(jìn)程造成阻塞,其他時(shí)候AOF后臺(tái)重寫都不會(huì)對(duì)阻塞父進(jìn)程,這將AOF重寫對(duì)服務(wù)器性能造成的影響降到了最低。