Redis中AOF持久化
Redis中除了提供了RDB持久化以外,還提供了AOF持久化 (append-only file)。RDB持久化是把鍵值對儲存在RDB檔案中,AOF是把伺服器中執行的命令(SET, SADD, RPUSH等)儲存在AOF檔案中。要恢復時,伺服器通過載入和執行AOF檔案中儲存的命令來還原伺服器關閉時的資料庫狀態。與RDB檔案相比,AOF檔案一般比較大,而且恢復速度比較慢。但是好處是相對的,因為RDB是每隔一段時間持久化,如果在這期間發生了故障,會丟失很多資料。
AOF持久化的實現
AOF持久化分為命令的追加,檔案的寫入,檔案的同步這三個步驟。
- 命令的追加:
伺服器在執行完一個寫命令時,會將被執行的寫命令追加到伺服器狀態中的aof_buf緩衝區的末尾。
struct redisServer
{
...
sds aof_buf;
...}
- AOF檔案的寫入:
redis 的伺服器程序是一個事件迴圈,這個迴圈中有檔案事件(負責接收客戶端的命令請求,以及向客戶端傳送命令回覆),有時間事件(負責執行一些定時函式(如RDB中的serverCron函式))。每次個迴圈都要考慮是否執行一個flushAppendOnlyFile(), 以便能夠將aof_buff緩衝區中的內容寫入和儲存到AOF檔案中。
寫函式是如下實現的(在aof.c檔案中):
write(server.aof_fd,server.aof_buf,sdslen(server .aof_buf));
它將server.aof_buf中的資料寫到server.aof_fd這個檔案描述符裡。
這裡要特別注意的是,寫入並不代表就是立即寫入aof這個檔案中。這裡有兩個概念,核心緩衝區和程序緩衝區。
aof_buf是伺服器程序緩衝區的資料,呼叫write()函式時,是將程序緩衝區的資料寫入核心緩衝區中,此時write()函式就返回了,而核心緩衝區的資料什麼時候寫入磁碟,需要的核心來說了算,通常核心會把要寫的資料暫時存在緩衝區中,積累到一定數量後再一 次寫入。有時會導致意外情況,比如斷電,核心還來不及把核心緩衝區中的資料寫道磁碟上,這些更新的資料就會丟失。
現代作業系統這樣做是有道理的:因為這樣能提高了磁碟的I/O效率(讀寫磁碟總是十分耗時的)。
- AOF的檔案同步
所以flushAppendOnlyFIle()函式還有個同步的操作,其實是呼叫了fsync()或者fdatasync()這樣的同步函式來將核心緩衝區的資料立即“沖洗”到磁碟上。
AOF檔案的載入和資料還原
伺服器讀入AOF檔案並重新執行一遍AOF檔案裡儲存的寫命令,就可以還原伺服器關閉前的資料庫狀態。這裡要注意的是還原時建立的一個不帶連線的偽客戶端。
AOF檔案的重寫
隨著伺服器執行時間的增加,AOF檔案中的內容會越來越多,檔案體積會越來越大,所以需要提供一個檔案重寫功能,用一個新的檔案,這個檔案不包含任何浪費空間的冗餘指令,來代替原來的舊檔案。這個新檔案的體積會小很多。
Redis伺服器程序在AOF檔案重寫時,fork()一個子程序,子程序擁有伺服器程序的所有副本,用它來執行檔案重寫。這樣伺服器程序還能繼續處理其他到來的命令。但是這樣會產生一個問題,在子程序執行重寫的過程中,伺服器也可能執行寫檔案的操作,這樣使得伺服器當前資料庫狀態和AOF檔案儲存的資料庫狀態不一致。
為了解決這個問題,在子程序執行重寫時,Redis又開啟了一個AOF重寫緩衝區,將執行的寫命令存在這個緩衝區裡,當子程序執行完之後,給父程序傳送一個訊號,父程序又將AOF重寫緩衝區的命令追加到新的AOF檔案中。
最後用新的AOF檔案代替舊的AOF檔案。重新工作完成。也就是BGREWRITEAOF命令的過程。