一、AOF(Append Only File):持久性記錄服務(wù)器接收到的每個(gè)寫操作。然后可以在服務(wù)器啟動(dòng)時(shí)再次重播這些操作,從而重建原始數(shù)據(jù)集。命令使用與 Redis 協(xié)議本身相同的格式進(jìn)行記錄;注意:AOF文件只會(huì)記錄Redis的寫操作命令,因?yàn)樽x命令對數(shù)據(jù)的恢復(fù)沒有任何意義1、AOF文件說明:*2
表示當(dāng)前命令有2個(gè)部分,每部分都是由$+數(shù)字
開頭,后面緊跟著具體的命令、鍵或值。數(shù)字
表示這部分中的命令、鍵或值一共有多少字節(jié)。例如,$6 SELECT
表示這部分有 6 個(gè)字節(jié),也就是SELECT
命令。
2、AOF日志的生成過程:Redis成功執(zhí)行寫操作指令,然后將寫的指令按照自定義格式追加到aof_buf
緩沖區(qū),這是第一個(gè)緩沖區(qū);Redis主進(jìn)程將aof_buf
緩沖區(qū)的數(shù)據(jù)寫入到內(nèi)核緩沖區(qū),這是第二個(gè)緩沖區(qū);根據(jù)AOF同步策略適時(shí)地將內(nèi)核緩沖區(qū)的數(shù)據(jù)同步到磁盤,過程結(jié)束。①命令傳播:Redis 將執(zhí)行完的命令、命令的參數(shù)、命令的參數(shù)個(gè)數(shù)等信息發(fā)送到 AOF程序中。
AOF日志寫入是在Redis成功執(zhí)行命令之后才進(jìn)行的,可避免記錄錯(cuò)誤指令情況,同時(shí)不會(huì)阻塞當(dāng)前寫操作。這樣做的風(fēng)險(xiǎn),如果在寫AOF日志時(shí)宕機(jī),會(huì)導(dǎo)致指令和相關(guān)參數(shù)丟失,這種情況一般是寫回磁盤的時(shí)機(jī)有關(guān),寫回磁盤的頻率越高,發(fā)生數(shù)據(jù)丟失的可能性就越小;另一個(gè)風(fēng)險(xiǎn)就是有可能阻塞下一個(gè)操作,這個(gè)和寫文件方式和時(shí)機(jī)有關(guān),如果Redis每次成功執(zhí)行指令之后都力圖將當(dāng)前指令同步到AOF文件,開銷必然很大。
因此Redis引入了緩沖區(qū)的概念,緩沖區(qū)對應(yīng)了文件的寫入方式(不求一步到位,允許循序漸進(jìn)地寫入),而何時(shí)將緩沖區(qū)的內(nèi)容徹底同步到文件就涉及到了AOF的同步策略(寫回磁盤的時(shí)機(jī))。②命令追加:在AOF開啟的情況下,Redis會(huì)將成功執(zhí)行的寫指令以上文我們講過的協(xié)議格式追加到Redis的aof_buf
緩沖區(qū)。aof_buf
緩沖區(qū)保存著所有等待寫入到AOF 文件的協(xié)議文本。
③文件寫入:Redis的主服務(wù)進(jìn)程本質(zhì)上是一個(gè)死循環(huán),循環(huán)中有負(fù)責(zé)接受客戶端的請求,并向客戶端發(fā)送回執(zhí)的邏輯。在AOF功能開啟的情況下,文件事件會(huì)將成功執(zhí)行之后的寫命令追加到aof_buf
緩沖區(qū),在主服務(wù)進(jìn)程死循環(huán)的最后,會(huì)調(diào)用flushAppendOnlyFile
函數(shù),該函數(shù)會(huì)將aof_buf
中的數(shù)據(jù)寫入到內(nèi)核緩沖區(qū),然后判斷是否應(yīng)該進(jìn)行同步。而是否進(jìn)行同步則是由Redis配置中的appendOnlyFile
選項(xiàng)來決定的
用戶調(diào)用write
函數(shù)將數(shù)據(jù)寫入到文件時(shí),操作系統(tǒng)內(nèi)核會(huì)將數(shù)據(jù)首先保存在內(nèi)存緩沖區(qū)中,等到緩沖區(qū)的空間被填滿或者到達(dá)一定的時(shí)機(jī)之后,內(nèi)核會(huì)將數(shù)據(jù)同步到磁盤。這種同步過于依賴于操作系統(tǒng)內(nèi)核,操作系統(tǒng)提供了fsync
和fdatasync
兩個(gè)同步函數(shù),可以強(qiáng)制內(nèi)核立即將緩沖區(qū)內(nèi)的數(shù)據(jù)同步到磁盤。偽代碼void eventLoop {
while(true){
// ...
// 文件事件,接受命令請求,返回客戶端回執(zhí)
// 根據(jù)aof功能是否開啟,決定是否將寫命令追加到aof_buf緩沖區(qū)
handleFileEvents();
// 將aof_buf數(shù)據(jù)寫入內(nèi)核緩沖區(qū)
// 判斷是否需要將數(shù)據(jù)同步到磁盤
flushAppendOnlyFile();
// ...
}
};
④文件同步:redis.conf
配置文件中appendOnlyFile
的選項(xiàng)有三個(gè)值可選,對應(yīng)三種AOF同步策略。
1)No
同步時(shí)機(jī)由內(nèi)核決定; 寫命令執(zhí)行完先把日志寫入AOF
文件的內(nèi)核緩沖區(qū),待系統(tǒng)緩存已滿或定期保存機(jī)制觸發(fā)或Redis或AOF關(guān)閉 才同步到磁盤,這個(gè)同步是阻塞的。寫入操作也是阻塞的。這個(gè)階段宕機(jī)由系統(tǒng)內(nèi)核及運(yùn)行環(huán)境確定,不確定性較大。
2)Everysec
每一秒鐘同步一次。也是寫道內(nèi)核緩存區(qū),間隔1秒同步到磁盤,有子線程執(zhí)行不會(huì)阻塞。
實(shí)際運(yùn)行中該模式對fsync
或fdatasync
的調(diào)用并不是每秒一次,而是和調(diào)用flushAppendOnlyFile
函數(shù)時(shí)Redis所處的狀態(tài)有關(guān)。
flushAppendOnlyFile
函數(shù)被調(diào)用時(shí), 可能會(huì)出現(xiàn)以下四種情況:
子線程正在執(zhí)行同步,同步的執(zhí)行時(shí)間未超過 2 秒,那么程序直接返回,如宕機(jī),損失數(shù)據(jù)在2秒內(nèi)的數(shù)據(jù)。 子線程正在執(zhí)行同步,同步已經(jīng)執(zhí)行超過 2 秒(超時(shí)),程序執(zhí)行寫入操作 ,但不執(zhí)行新的同步操作 。這時(shí)的寫入操作必須等待子線程先完成原本的同步操作 ,因此這里的寫入操作會(huì)比平時(shí)阻塞更長時(shí)間,此時(shí)宕機(jī),損失數(shù)據(jù)超過兩秒。
子線程沒有在執(zhí)行同步,上次成功執(zhí)行同步距今不超過1秒,那么程序執(zhí)行寫入,但不執(zhí)行同步
子線程沒有在執(zhí)行同步,上次成功執(zhí)行同步距今已經(jīng)超過1秒,那么程序執(zhí)行寫入和同步 3)Always
每執(zhí)行一個(gè)命令同步一次。同步操作是由 Redis 主進(jìn)程執(zhí)行的,所以在同步執(zhí)行期間,主進(jìn)程會(huì)被阻塞,不能接受命令請求。
二、AOF文件的載入和數(shù)據(jù)還原:AOF文件中包含了能夠重建數(shù)據(jù)庫的所有寫命令,因此將所有命令讀入并依次執(zhí)行即可還原Redis之前的數(shù)據(jù)狀態(tài)。1、創(chuàng)建一個(gè)不帶網(wǎng)絡(luò)連接的偽客戶端(fake client),偽客戶端執(zhí)行命令的效果, 和帶網(wǎng)絡(luò)連接的客戶端執(zhí)行命令的效果完全相同;
2、讀取AOF所保存的文本,并根據(jù)內(nèi)容還原出命令、命令的參數(shù)以及命令的個(gè)數(shù);
3、根據(jù)指令、指令的參數(shù)等信息,使用偽客戶端執(zhí)行命令。
4、執(zhí)行 2 和 3 ,直到AOF文件中的所有命令執(zhí)行完畢。
三、AOF重寫:AOF的作用是幫我們還原Redis的數(shù)據(jù)狀態(tài),其中包含了所有的寫操作,但是正常情況下客戶端會(huì)對同一個(gè)KEY
進(jìn)行多次不同的寫操作;這樣被頻繁操作的鍵有很多的話,AOF文件的體積就會(huì)急速膨脹。
AOF文件的體積受操作系統(tǒng)大小的限制,本身就不能無限增長;體積過于龐大的AOF文件會(huì)影響指令的寫入速度,阻塞時(shí)間延長;AOF文件的體積越大,Redis數(shù)據(jù)恢復(fù)所需的時(shí)間也就越長。
Redis提供了rewrite
的AOF重寫
功能來精簡AOF文件體積
四、AOF重寫的實(shí)現(xiàn)原理:AOF文件的生成是讀取Redis當(dāng)前的數(shù)據(jù)狀態(tài)來重新生成的.重寫過程是由子進(jìn)程執(zhí)行bgrewriteaof
來完成的。這樣處理的最大好處是:AOF重寫期間,不影響主進(jìn)程處理命令請求;子進(jìn)程帶有主進(jìn)程的數(shù)據(jù)副本,操作效率更高.
如果使用線程,在處理共享內(nèi)存的時(shí)候,必須使用同步對象,這樣會(huì)影響其性能。
使用子進(jìn)程,會(huì)使用寫時(shí)拷貝,fork子進(jìn)程的時(shí)候,子進(jìn)程會(huì)拷貝父進(jìn)程的頁表,即虛實(shí)映射關(guān)系,不是物理內(nèi)存;父子進(jìn)程一方寫操作,觸發(fā)寫時(shí)拷貝機(jī)制,于是父子進(jìn)程就有了獨(dú)立的數(shù)據(jù)副本,就不用加鎖來保證數(shù)據(jù)安全。
fork
子進(jìn)程的過程中,父進(jìn)程的頁表越大阻塞的時(shí)間也越長,不過通常而言該過程是非???。
fork
完子進(jìn)程后,如果父子進(jìn)程任意一方修改了共享數(shù)據(jù),就會(huì)發(fā)生**「寫時(shí)復(fù)制」**,這期間會(huì)拷貝物理內(nèi)存,如果內(nèi)存越大,自然阻塞的時(shí)間也越長;這里復(fù)制粒度是一個(gè)內(nèi)存頁,如果只是修改一個(gè)256B的數(shù)據(jù),父進(jìn)程需要讀原來的整個(gè)內(nèi)存頁,然后再映射到新的物理地址寫入。一讀一寫會(huì)造成讀寫放大。如果內(nèi)存頁越大(例如2MB的大頁),那么讀寫放大也就越嚴(yán)重,對Redis性能造成影響。因此使用Redis的AOF功能時(shí),需要注意頁表的大小不要設(shè)置的太大。 子進(jìn)程在進(jìn)行 AOF 重寫期間主進(jìn)程還需要繼續(xù)處理命令,而新的命令可能對現(xiàn)有的數(shù)據(jù)進(jìn)行修改, 會(huì)讓當(dāng)前數(shù)據(jù)庫的數(shù)據(jù)和重寫后的 AOF 文件中的數(shù)據(jù)不一致,此時(shí)Redis引入了另一個(gè)緩沖區(qū)的概念——AOF重寫緩沖區(qū)。
因此當(dāng)子進(jìn)程在執(zhí)行AOF重寫(bgrewriteaof
)時(shí), 主進(jìn)程需要執(zhí)行以下三個(gè)工作:處理客戶端的命令請求;將寫命令追加到AOF緩沖區(qū)
(aof_buf
);將寫命令追加到AOF重寫緩沖區(qū)
。
當(dāng)子進(jìn)程完成 AOF重寫之后, 它會(huì)向父進(jìn)程發(fā)送一個(gè)完成信號, 父進(jìn)程在接到完成信號之后, 會(huì)調(diào)用一個(gè)信號處理函數(shù), 并完成以下工作:
將 AOF重寫緩沖區(qū)中的內(nèi)容全部寫入到新AOF 文件中;完畢之后, 現(xiàn)有 AOF 文件、新 AOF 文件和數(shù)據(jù)庫三者的狀態(tài)就完全一致了
對新的 AOF 文件進(jìn)行改名,覆蓋原有的 AOF 文件。注意,這是一個(gè)原子操作,改名過程中不接受客戶端指令。完畢之后, 程序就完成了新舊兩個(gè) AOF 文件的交替。主進(jìn)程就可以繼續(xù)像往常一樣接受命令請求了。 在整個(gè) AOF 后臺重寫過程中, 只有將AOF重寫緩沖區(qū)數(shù)據(jù)寫入新AOF文件和改名操作會(huì)造成主進(jìn)程阻塞, 其他時(shí)候, AOF 后臺重寫都不會(huì)對主進(jìn)程造成阻塞, 這將 AOF 重寫對性能造成的影響降到了最低。
五、AOF后臺重寫觸發(fā)條件
1、AOF的其他兩個(gè)配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
2、AOF 重寫可以由用戶通過調(diào)用 bgrewriteaof
手動(dòng)觸發(fā)。
3、服務(wù)器在 AOF 功能開啟的情況下, 會(huì)維持以下三個(gè)變量:
記錄當(dāng)前 AOF 文件大小的變量 aof_current_size;
記錄最后一次 AOF 重寫之后, AOF 文件大小的變量 aof_rewrite_base_size;
增長百分比變量 aof_rewrite_perc
4、當(dāng)Redis中的定時(shí)函數(shù) serverCron
執(zhí)行時(shí), 它都會(huì)檢查以下條件是否全部滿足, 如果是的話, 就會(huì)觸發(fā)自動(dòng)的 AOF 重寫:沒有 bgsave
命令在進(jìn)行。沒有 bgrewriteaof
在進(jìn)行。當(dāng)前 AOF 文件大小大于 我們設(shè)置的auto-aof-rewrite-min-size
。當(dāng)前 AOF 文件大小和最后一次 AOF 重寫后的大小之間的比率大于等于指定的增長百分比auto-aof-rewrite-percentage
。
默認(rèn)情況下, 增長百分比為 100%
, 也即是說, 如果前面三個(gè)條件都已經(jīng)滿足, 并且當(dāng)前 AOF 文件大小比最后一次 AOF 重寫時(shí)的大小要大一倍的話, 那么觸發(fā)自動(dòng) AOF 重寫。
六、總結(jié):
AOF是將Redis的所有寫日志同步到磁盤的一種持久化方法,通過執(zhí)行AOF中記錄的所有指令可以達(dá)到恢復(fù)Redis原始數(shù)據(jù)狀態(tài)的目的。
對于指令的同步時(shí)機(jī),Redis提供了三種AOF同步策略,分別是No
,Everysec
,Always
,三種策略對Redis性能的負(fù)面影響是由低到高的,在數(shù)據(jù)可靠性上也是由低到高的。
為了解決AOF日志太大的問題,Redis提供了AOF重寫的機(jī)制,利用「寫時(shí)復(fù)制」和「AOF重寫緩沖區(qū)」達(dá)到精簡AOF文件的目的。