還不懂 redis 持久化?看看這個


Redis 是一個內存數據庫,為了保證數據不丟失,必須把數據保存到磁盤,這就叫做持久化。

Redis 有兩種持久化方法: RDB 方式以及 AOF 方式

RDB 持久化

前言

RDB持久化把內存中的數據持久化到硬盤,生成RDB文件,也就是“快照”。通過RDB文件可以重新恢復內存中的數據。RDB文件是一個壓縮過的文件,因此RDB持久化的體積比AOF小,因為存儲的是數據,所以恢復很快,性能好,但是可能會丟失最后一次持久化的數據。

RDB持久化機制的兩種方式

顯示開啟持久化

Save命令

SAVE命令會阻塞Redis服務器的主進程,直到RDB文件創建完畢。在服務器進程阻塞期間,服務器不能處理任何命令請求

BGSAVE命令

BGSAVE命令會fork一個子進程,由子進程負責創建RDB文件,父進程繼續處理命令請求

  • 子進程執行BGSAVE命令期間,SAVE、BGSAVE命令會被父進程拒絕。因為Redis不允許兩個進程同時進行持久化
  • BGSAVE和BGREWRITEAOF命令同樣不能同時執行,性能考慮

自動持久化機制

通過配置文件redis.conf

# 多久持久化一次🐲🐲
# 900秒內有一個key改動 300秒內10個  60秒內10000個
save 900 1
save 300 10
save 60 10000

底層實現:

  • Redis 服務器的周期性操作函數 serverCron 默認每100ms執行一次,該函數用戶對正在執行的服務器進行維護,它的其中一項工作就是檢查save 選項所設置的持久化條件是否滿足,滿足的話執行 BGSAVE 命令

  • Redis 使用 saveparams 數組保存持久化條件

    image-20210505162918183

  • dirty 計數器 和 lastsave 記錄上一次持久化到現在修改了幾次和上一次執行SAVE或者BGSAVE的時間戳

RDB文件的結構

image-20210505163948798

參考《REDIS設計與實現》P125

后續補充,先說一下目前總結,其實也就是databases的結構:

redis 有多個數據庫,如果每個數據庫都為空,那么databases也為空。如果不是的話,database的結構差不多是這樣的:

image-20210505164412735

key_value_pairs 部分保存了該數據庫中所有的鍵值對數據,如果鍵值對帶有過期時間,過期時間也會和鍵值對保存在一起。會根據value的不同類型采用不同的結構存儲

接下來看一個完整的 RDB 文件結構示例,包含0號數據庫和3號數據庫

image-20210516101414117

key_value_pairs 字段詳解~(挖個坑)

分析RDB文件

使用od -c dump.rdb命令根據rdb文件的結構人工分析,或者使用工具 redis-dump-check 分析。分析 RDB 文件其實沒有實際的意義,但可以幫助我們進一步理解 RDB 文件的結構。

AOF持久化

引言

AOF持久化與RDB持久化不同,RDB持久化把內存中的數據按照某種格式存儲到硬盤中,而AOF持久化是把所有寫命令記錄到硬盤,恢復的時候需要重新執行所有寫命令。

在這個AOF文件里面,除了用於指定數據庫的 SELECT 命令是服務器自動添加的之外,其他都是我們之前通過客戶端發送的命令。

AOF 持久化的實現機制

AOF 持久化的實現有三步:

  • 命令追加:每執行完一個寫命令后,會以協議格式將被執行的寫命令追加到服務器內核的 aof_buf 緩沖區的末尾
  • 命令寫入:操作系統調用 write 函數,將aof_buf 緩沖區的數據排入隊列,晚些時候一起寫入磁盤
  • 命令同步:操作系統調用 fsync 和 fdatasync 兩個同步函數,強制讓操作系統立即把隊列中的數據寫入磁盤

總的來說其實就是每執行完一次寫命令(也叫做一個事件循環),redis 都會把這條命令格式化后寫到緩沖區,再從緩沖區持久化到磁盤。但是你可能會有疑問,這不是兩步嗎,為什么 write 函數不能直接把緩沖區的數據同步到磁盤,這就涉及 Linux 內核關於文件 IO 的知識了,這里簡單解釋一下。

為了提高文件的寫入效率,在現代操作系中,當用戶調用 write函數,將一些數據寫入到文件的時候,操作系統通常會將寫入數據暫時保存在一個內存緩沖區(隊列)里面, 等到隊列的空間被填滿、或者超過了指定的時限之后,才真正地將隊列中的數據寫入到磁盤里面。這種方式又叫做延遲寫。

如何開啟和配置 AOF 持久化

同樣是配置 redis.conf 文件

# 是否開啟aof持久化🐲🐲
appendonly no

# aof持久化文件名
appendfilename "appendonly.aof"

# aof持久化策略 默認是一秒持久化一次🐲🐲
# appendfsync always
appendfsync everysec
# appendfsync no

上述的持久化策略就是配置 命令同步 的時機。

  • no:只有等隊列滿了數據才會持久化到磁盤。如果此時服務器宕機了,可能會丟失上次同步之后的所有命令數據。效率最高。
  • always:每寫入一條命令就要同步一次,安全性最高,最多丟失一條命令的數據。但是效率最低。
  • everysec:每一秒鍾執行同步一次,最多丟失一秒的數據。

如何載入AOF文件還原數據

也就是如何通過AOF文件還原數據庫的過程

  • 創建一個不帶網絡連接的偽客戶端。因此redis命令只能在客戶端的上下文執行
  • 從AOF 文件中分析一條寫命令
  • 客戶端執行命令,循環2 3 過程

AOF重寫機制

引言

隨着服務器運行時間的流逝,可能會頻繁的對同一個 key 進行很多寫操作,AOF 文件中的內容會越來越多,文件越來越大,如果不加以控制,會對服務器造成影響,還原數據的時間也加長。

比如現在有一個鍵值對為list類型,在服務器運行時間,你對這個key進行了十次寫操作。AOF在持久化時就要保存十條命令,但是我們只保存一條命令也可以還原此key的狀態。

重寫機制就是這樣實現的:Redis 服務器創建一個新的 AOF 文件替代現有的 AOF 文件,新舊兩個文件保存的數據庫狀態相同,但是新的 AOF 文件不會包含浪費空間的冗余命令,只保存必須的命令,因此新的AOF 體積就要小得多。

觸發方式

  • 手動觸發

    使用BGREWRITEAO命令,fork 子進程重寫aof

  • 自動觸發,配置redis.conf

    # 重寫aof文件的配置🐲🐲
    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb
    

實現機制

通過讀取服務器當前數據庫狀態來實現的,不依賴舊的 AOF 文件。

如果服務器想要用最小的命令來記錄 key 的狀態,最簡單高效的方法就是讀取 key 的值,記錄一條插入(add、push ....)的命令。一條就完事,不用在意以前的寫命令是什么。

因為AOF重寫會進行大量的寫入操作,因此 Redis 使用單個子進程來處理命令請求實現AOF重寫,縮小AOF文件體積。

為什么是進程不是線程呢?

  • 子進程重寫期間,服務器父進程可以繼續處理命令請求
  • 子進程有服務器進程的數據副本,不會導致線程安全問題

總結

Redis 持久化有RDB持久化和AOF持久化兩種,RDB效率高但是可能會丟失大量數據,安全性較低,AOF效率低但是安全性高。

Redis 默認使用的持久化方式是RDB持久化

AOF 重寫的目的是為了減小aof文件的體積

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM