Redis數據庫的持久化

    本文出自  Redis的設計與實現  黃建宏 一書.只是對這部分內容進行了一點小整理.

    作爲內存數據庫,在運行時,Redis會將數據存儲到內存中.也就是說,進程一旦退出,Redis存儲的數據就會消失.爲了解決這個問題,Redis提供了RDB,AOF持久化功能.

    通俗地來講.RDB 持久化功能是通過保存數據庫 鍵值對 來記錄數據庫的狀態不同; AOF 持久化是通過保存服務器所 執行的命令 來記錄數據庫的狀態不同.

RDB:

    RDB持久化既可以手動執行,也可以根據配置文件的配置執行.

    手動執行有兩個命令,一個是 SAVE, 另一個是 BGSAVE.

    其中 SAVE 命令會阻塞 Redis 服務進程,直到RDB文件創建完爲止,在這個阻塞期間,服務不能接收其他任何命令.

    而 BGSAVE 命令會派生一個子進程,不會阻塞其他命令的執行,當 BGSAVE  執行期間,客戶端再發送 SAVE 或 BGSAVE 會被服務器拒絕執行.因爲服務器禁止這兩個命令同時執行,以免產生競爭.

    創建 RDB 文件的工作實際由 rdb.c 文件下的 rdbSave 函數來執行, SAVE 和 BGSAVE  會以不同的方式調用這個函數.

    RDB 文件的載入一般是在 Redis 服務載入時自動執行.一般啓動時,只要檢測到配置文件夾下存在rdb文件,就會自動執行.一般可以看到 DB loaded from disk: ... 就是執行打印的日誌.且無論何時,載入rdb文件期間服務是被阻塞的.

    除了手動執行外,簡單的方式是通過配置文件自動間隔性執行,只需按需修改 SNAPSHOTTING 下的 save,stop-writes-on-bgsave-error,dbfilename,dir等屬性.每個屬性具體含義可查看屬性上方相應註釋.我們以默認配置爲例:

save 900 1
save 300 10
save 60 10000

    只要滿足下面條件任何一個, BGSAVE 命令就會執行:

    1:服務器900秒內對數據庫進行了至少1次修改

    2:服務器300秒內對數據庫進行了至少10修改

    3:服務器60秒內對數據庫進行了至少10000修改

    服務器會根據設置的保存條件,將條件設置爲服務器狀態的 redisServer 結構的 saveparams 屬性:

struct redisServer {

  ***
  
  // 記錄保存條件的數組
  struct saveparam *saveparams;

  ***

}

    saveparams 是一個數組,數組中保存着 saveparam 結構,每個 saveparam 結構都保存着一個 save 的數據:

struct saveparam {

  time_t seconds;

  int changes;

}

    除此之外,服務器狀態 redisServer 還維持着一個 dirty 計數器 和一個 lastsave 屬性

    dirty 是記錄上一次成功執行 SAVE 或者 BGSAVE 之後,服務器對數據進行了多少次修改.

    lastsave 是一個 UNIX 時間戳,記錄了上一次執行 SAVE 或 BGSAVE 的時間.

    要想做到自動間隔性執行 RDB 持久化,還需要藉助 Redis 週期性執行的函數 serverCorn,這個函數每隔 100毫秒執行一次,用於對正在進行的服務進行維護,其中就包括遍歷並檢查 save 保存是否滿足來決定是否執行 BGSAVE 操作.

AOF:

    相對於 RDB ,通常 AOF 比 RDB 執行頻率更高,且開啓了 AOF 時,將會優先使用 AOF 進行持久化,而不會使用 RDB.

    服務器在啓動時,可以載入 AOF 文件中保存的命令來還原服務器關閉之前的數據狀態.一般可以看到日誌

    DB loaded from append only file: ...

    可以通過命令 BGREWRITEAOF 手動進行 AOF 的持久化,也可以配置 redis.conf 文件修改屬性 appendonly爲 yes,可以通過屬性 appendfilename 屬性來指定文件名稱,appendfsync 來指定何時寫入和同步AOF 文件,no-appendfsync-on-rewrite來指定 AOF 重寫( rewrite )時是否暫緩文件同步.等屬性實現 AOF 的自動化.

    AOF 功能的實現可以分爲 命令追加,文件寫入,文件同步三個步驟.當 AOF 開啓式,服務器每次執行完畢一個命令後,會以協議的格式將被執行的命令已追加的方式保存在服務器狀態的 redisServer 結構 aof_buf 緩衝區末尾.

   

struct redisServer {

  ***
  
  // AOF 緩衝區
  sds aof_buf;

  ***

}

    當定時運行的函數 serverCorn 執行 AOF 的寫操作時,如果有一些數據已經被寫到 aof_buf 緩衝區,會調用 flushAppendOnlyFile() 函數來判斷需不需要將 aof_buf 緩衝區的文件持久化道 AOF 文件之中.flushAppendOnlyFile()的行爲有服務器狀態的 redisServer 結構配置的 appendfsync 的值來決定,值不同flushAppendOnlyFile()的行爲就不同.

    其值爲 always 時: 將aof_buf 緩衝區的所有內容寫入並同步到 AOF 文件中.效率最慢,但是最安全.

    everysec:將aof_buf 緩衝區的所有內容寫入到 AOF 文件中,如果上次同步 AOF 文件時間距離現在間隔1s,則開一個線程進行同步.效率相對較快,但可能會丟失1s的數據

    no: 將aof_buf 緩衝區的所有內容寫入到 AOF 文件中,不執行同步,合適同步由操作系統來決定.效率最快,但同步時間一般最長,出現故障停機時,會丟失上次同步 AOF 到停機時的所有數據.

    appendfsync 默認值爲 everysec.

    AOF 持久化保存被執行的命令來記錄數據庫的狀態的,隨着時間的流逝, AOF 可能會過於臃腫,同步時間就會變得很長,會對 Redis 性能造成影響,爲了解決這個問題,Redis 提供了文件重寫( rewrite ) 功能.Redis 服務會創建一個新的 AOF 文件代替舊的 AOF 文件,新的文件不會含有任何浪費空間的冗餘命令.比如:

    rpush list 'a','b'

    rpush list 'c','d','e'

    lpop list

    lpop list 

    此時數據庫的數據應該是 'c','d','e',也就是說,重寫過後的 AOF 文件只包含 rpush list 'c','d','e' 命令,這樣就能有效的縮小 AOF 文件的大小.在進行 AOF 重寫時,不需要對現有的 AOF 文件進行任何的操作,包括讀取.而是直接讀取數據庫的狀態進行重寫操作.可以通過修改配置文件來定製化重寫.比如 

    auto-aof-rewrite-percentage 100  AOF 文件增長了原大小的 100%時,即由50mb變爲100mb就進行重寫
    auto-aof-rewrite-min-size 64mb  AOF文件64mb時即重寫,可以適當增加

    爲防止阻塞, AOF  重寫是 Redis 服務開啓了一個子進程來進行的,如果在重寫的過程中,有新的命令對數據庫進行了修改,就可能造成數據不一致的情況.Redis設置了一個 AOF重寫緩衝區 來解決這個問題.當Redis服務進行重寫時,如果出現新的命令,就會把這個命令加入到 AOF 緩衝區 和 AOF 重寫緩衝區,當重寫緩衝區命令全部重寫後, AOF 重寫操作纔可以完成.

     

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章