「進擊Redis」二12、超詳細解析 Redis 持久化之 RDB

前言

從上篇 RedisTemplate 可沒你想的那麼簡單 完結後,整個 Redis 的客戶端相關的就弄完了,主要是JedisLettuceRedisTemplate 三篇。有不熟悉的好哥哥能夠去個人文章那裏翻翻,說不定就會有不同的收穫。今天的這篇的話是關於 Redis 持久化相關的第一篇,後續應該會有幾篇關於持久化的,好哥哥們好好看,主要是要動手操做起來。固然若是隻是爲了應付面試的話能夠大概的看看,可是仍是建議這一塊相關的東西系統的看看。看完記得點贊加關注喲。
面試

概述

Redis 系列第一篇 初識 Redis 中就有提到說 Redis 是一個基於內存的非關係性數據庫。那所謂內存數據庫,就是將數據庫中的內容保存在內存中,這與傳統的MySQLOracle等關係型數據庫直接將內容保存到硬盤中相比,內存數據庫的讀寫效率比傳統數據庫要快的多(內存的讀寫效率遠遠大於硬盤的讀寫效率)。可是保存在內存中也隨之帶來了一個缺點,一旦斷電或者宕機,那麼內存數據庫中的數據將會所有丟失。
爲了解決這個缺點,Redis 提供了將內存數據持久化到硬盤,以及用持久化文件來恢復數據庫數據的功能。Redis 支持兩種形式的持久化,一種是RDB快照(snapshotting),另一種是AOF(append-only-file)。redis

RDB

rdb
RDB持久化是把當前進程數據生成快照保存到硬盤的過程。可以在指定時間間隔內對數據進行快照存儲,默認狀況下,Redis 將數據快照保存在dump.rdb這個二進制的文件中。觸發RDB持久化過程分爲手動觸發和自動觸發。算法

觸發機制

上面有提到觸發分爲手動觸發和自動觸發。手動觸發又分紅了兩種方式,第一種使用save,第二種使用bgsaveshell

手動觸發-save

save
save會阻塞當前 Redis 服務器,直到RDB過程完成爲止,對於內存比較大的實例會形成長時間阻塞,線上環境不建議使用。命令以下:數據庫

127.0.0.1:6379> save
OK
複製代碼

手動觸發-bgsave

上面提到了就是save是一個阻塞操做。假設在生產環境中使用這個命令,若是數據量夠大,那麼會使 Redis 服務器阻塞的時間很是長,致使 Redis 服務的不可用。bgsave 就是用來解決這個問題的,bgsave 執行時,Redis 進程會執行fork操做建立子進程,RDB 持久化過程由子進程負責,完成後自動結束。阻塞只發生在 fork 階段,通常時間很短。數組

127.0.0.1:6379> bgsave
Background saving started
複製代碼

整個過程以下:
在這裏插入圖片描述服務器

save-bgsave 對比

命令 save bgsave
類型 同步 異步
是否阻塞 只有在 fork 是阻塞的
優勢 沒有額外的內存消耗 不會阻塞服務器
缺點 阻塞服務器 須要 fork 建立子進程,額外消耗內存

自動觸發

自動RDB
除了執行命令手動觸發以外,Redis 內部還存在自動觸發RDB的持久化機制。觸發的邏輯是 Redis 在 N 秒內有 M 個鍵被改動,好哥哥們能夠經過修改配置文件來實現對RDB的自動觸發。默認配置以下:markdown

## 表示 900 秒內若是至少有 1 個 key 的值變化,則觸發
save 900 1
## 表示300 秒內若是至少有 10 個 key 的值變化,則觸發
save 300 10
## 表示60 秒內若是至少有 10000 個 key 的值變化,則觸發
save 60 10000
複製代碼

其餘 RDB 相關配置:網絡

## bgsave寫入錯誤是否中止寫入
stop-writes-on-bgsave-error yes
## 是否對rdb文件使用壓縮格式
rdbcompression yes
## 是否對rdb文件校驗
rdbchecksum yes
## rdb持久化名稱
dbfilename dump.rdb
## rdb持久化存放目錄
dir ./
複製代碼
自動觸發原理

Redis 服務器維護了一個狀態結構,其中包括save條件的數組(saveparams)計數器(dirty)上次保存時間(lastsave)。前面咱們在 redis.conf配置文件中進行了關於save 的配置,解析成條件的數組後就是下面的樣子: saveparams
當服務器成功執行一次修改操做,那麼計數器(dirty)就會加 1。而lastsave 屬性記錄上一次執行savebgsave的時間,Redis 服務器還有一個週期性操做函數 severCron ,默認每隔 100 毫秒就會執行一次,該函數會遍歷並檢查 saveparams 數組中的全部保存條件,只要有一個條件被知足,那麼就會執行 bgsave 命令。 執行完成以後,計數器(dirty)更新爲 0 ,lastsave也更新爲執行命令的完成時間。app

數據恢復

假設哪天有哪位好哥哥執行了FLUSHALL 命令(千萬別再生產上使用這個命令哦),把全部數據都清了,這時不要慌將備份文件dump.rdb (前提是你的快照生成的時機在FLUSHALL以前) 移動到 Redis 安裝目錄並啓動服務便可,Redis 就會自動加載文件數據至內存了。Redis 服務器在載入 RDB 文件期間,會一直處於阻塞狀態,直到載入工做完成爲止。獲取 Redis 的安裝目錄可使用下面的命令。

config get dir
複製代碼

RDB文件解析

看這個文件我已經快瞎了,因此我在解釋前面都加了行號(夠貼心了吧,這還不點贊加關注嗎)。

➜  go-redis-parser od -A x -t x1c -v ./teststub/dumpV9.rdb
000000  52  45  44  49  53  30  30  30  39  fa  09  72  65  64  69  73
         R   E   D   I   S   0   0   0   9 372  \t   r   e   d   i   s
000010  2d  76  65  72  05  35  2e  30  2e  35  fa  0a  72  65  64  69
         -   v   e   r 005   5   .   0   .   5 372  \n   r   e   d   i
000020  73  2d  62  69  74  73  c0  40  fa  05  63  74  69  6d  65  c2
         s   -   b   i   t   s 300   @ 372 005   c   t   i   m   e 302
000030  71  8a  8d  5d  fa  08  75  73  65  64  2d  6d  65  6d  c2  30
         q 212 215   ] 372  \b   u   s   e   d   -   m   e   m 302   0
000040  e0  0f  00  fa  0c  61  6f  66  2d  70  72  65  61  6d  62  6c
       340 017  \0 372  \f   a   o   f   -   p   r   e   a   m   b   l
000050  65  c0  00  fe  00  fb  06  00  f9  00  00  01  73  01  61  f9
         e 300  \0 376  \0 373 006  \0 371  \0  \0 001   s 001   a 371
000060  03  0e  02  6c  69  01  11  11  00  00  00  0d  00  00  00  02
       003 016 002   l   i 001 021 021  \0  \0  \0  \r  \0  \0  \0 002
000070  00  00  01  61  03  01  62  ff  f9  00  02  03  73  65  74  02
        \0  \0 001   a 003 001   b 377 371  \0 002 003   s   e   t 002
000080  01  62  01  61  f9  00  0f  06  73  74  72  65  61  6d  01  10
       001   b 001   a 371  \0 017 006   s   t   r   e   a   m 001 020
000090  00  00  01  6d  70  b5  4a  7e  00  00  00  00  00  00  00  00
        \0  \0 001   m   p 265   J   ~  \0  \0  \0  \0  \0  \0  \0  \0
0000a0  40  52  52  00  00  00  18  00  03  01  00  01  02  01  84  6e
         @   R   R  \0  \0  \0 030  \0 003 001  \0 001 002 001 204   n
0000b0  61  6d  65  05  83  61  67  65  04  00  01  02  01  00  01  00
         a   m   e 005 203   a   g   e 004  \0 001 002 001  \0 001  \0
0000c0  01  87  4c  61  6d  62  65  72  74  08  1d  01  05  01  02  01
       001 207   L   a   m   b   e   r   t  \b 035 001 005 001 002 001
0000d0  f2  33  8c  00  04  00  01  84  4a  61  63  6b  05  1a  01  05
       362   3 214  \0 004  \0 001 204   J   a   c   k 005 032 001 005
0000e0  01  02  01  f2  9c  ad  00  04  00  01  83  54  6f  6d  04  1e
       001 002 001 362 234 255  \0 004  \0 001 203   T   o   m 004 036
0000f0  01  05  01  ff  03  81  00  00  01  6d  70  b5  f8  1a  00  01
       001 005 001 377 003 201  \0  \0 001   m   p 265 370 032  \0 001
000100  05  67  72  6f  75  70  00  00  00  00  f9  00  0c  04  7a  73
       005   g   r   o   u   p  \0  \0  \0  \0 371  \0  \f 004   z   s
000110  65  74  15  15  00  00  00  12  00  00  00  04  00  00  01  61
         e   t 025 025  \0  \0  \0 022  \0  \0  \0 004  \0  \0 001   a
000120  03  f2  02  01  62  03  f3  ff  f9  03  0d  01  68  11  11  00
       003 362 002 001   b 003 363 377 371 003  \r 001   h 021 021  \0
000130  00  00  0d  00  00  00  02  00  00  01  61  03  01  61  ff  ff
        \0  \0  \r  \0  \0  \0 002  \0  \0 001   a 003 001   a 377 377
000140  0e  e0  f7  31  2f  37  16  df
       016 340 367   1   /   7 026 337
000148
複製代碼

這是一個 version 9RDB 文件,主要表達內容以下:

魔數(Magic Number)

在第 000000前 9 個字節是一個魔數,5 個字節 REDIS 和 4 個字節的版本號 0009。

輔助字段(Aux Fields)

通用字符串字段,用於向 RDB 添加狀態,Version 7 加入的,向後兼容。AUX 字段由鍵和值兩個字符串組成。主要有如下字段:

  1. redis-ver:版本號
  2. redis-bits:OS Arch
  3. ctime:RDB 文件建立時間
  4. used-mem:使用內存大小
  5. repl-stream-db:在 server.master 客戶端中選擇的數據庫
  6. repl-id:當前實例 replication ID
  7. repl-offset:當前實例複製的偏移量

數據庫索引

000050fe(0xfe)fb(0xfb)是 10 進制的 254 和 251,在 RDB 中國分別對應SELECT_DBRESIZE_DB。 每個 SELECTDB後都會緊跟着RESIZEDB,後者表示的是當前數據庫hashtable鍵大小的提示,每次切換數據庫時提早讀到,避免沒必要要的rehash

數據庫鍵值對

這裏的話就列舉基礎數據類型

String

00005000(0x00)是 10 進制的 0,表示String類型的對象,01 指key是一個字節長度:svalue 也是一個字節長度:a

List

0000600e(0x0e)是 10 進制的 14,表示List類型的對象。 2 個字節長度的keyli,接下來分別是一個長度的itemsab

Set

000070002,表示 Set 類型對象,3 個長度 set,兩個membersab

ZSet

0001000c 是 10 進制的 12,以ziplist結構存儲的Sortedset 類型,4 個長度的key:zset,接下來的 4 表示:4 個元素,zset會將memberscore一塊兒保存,因此,就是 2 組members;分別是:{memeber:"a",score:1}{member:"b",score:2}

Hash

0001200d(13)RDB_TYPE_HASH_ZIPLIST,表示是ziplist存儲的hash類型數據,1 個長度的 key:hkey後面的 2,存着 hash結構的 fieldvalue;在 fieldvalue 前面的 1,分別是指各自的長度,都是 a

文件 EOF

000130ff(255) EOF,在全部數據寫完結束後,會以一個EOF 結尾。

CheckSum

Version 5 開始,若是在配置文件中開啓rdbchecksum yes(上面解釋過這個配置),會在RDB文件的結尾處,用 8 個字節記錄,經過CRC64算法計算的整個文件內容的校驗和。

注意點

  1. RDB文件保存在dir配置指定的目錄下,文件名經過dbfilename配置指定。能夠經過執行config set dir{newDir}config setdbfilename{newFileName}運行期動態執行,當下次運行時RDB文件會保存到新目錄。
  2. 當遇到壞盤或磁盤寫滿等狀況時,能夠經過config set dir{newDir}在線修改文件路徑到可用的磁盤路徑,以後執行bgsave進行磁盤切換,一樣適用於AOF持久化文件。
  3. Redis 默認採用 LZF 算法對生成的RDB文件作壓縮處理,壓縮後的文件遠遠小於內存大小,默認開啓,能夠經過參數config set rdbcompression{yes|no}動態修改。
  4. 雖然壓縮RDB會消耗CPU,可是能大幅下降文件的大小,方便保存到硬盤或經過網絡發送給從節點,所以線上建議開啓。

優勢

  1. RDB文件是單一緊湊的二進制文件,比較適合作冷備,全量複製的場景。能夠將一個時間內產生的RDB文件複製到本地或者存儲到雲端,這樣能夠產生多個不一樣時間段內的數據備份,從而達到容災恢復。
  2. RDB對 Redis 對外提供的讀寫服務,影響很是小,可讓 Redis 保持高性能,由於 Redis 主進程只須要fork一個子進程,讓子進程執行磁盤IO操做來進行RDB持久化便可。
  3. 相對於AOF(下篇會弄它)持久化機制來講,直接基於 RDB 數據文件來重啓和恢復 Redis 進程,更加快速,由於RDB存儲的就是數據,而AOF存儲的是操做命令。

缺點

  1. 在 Redis 故障時,RDB 持久化丟失的數據可能會比AOF多。由於RDB是按照時間保存的快照,因此若是在這個時間段內沒有執行RDB操做,那這段時間的數據就丟失了。
  2. 耗時、耗性能,RDB 持久化一般都要fork一個子進程來執行。當數據量較大時,fork的過程是比較耗時的。
  3. RDB沒法實現實時或者秒級持久化。

本期就到這啦,有不對的地方歡迎好哥哥們評論區留言,另外求關注、求點贊

下一篇: Redis 持久化之 AOF 原理解析
上一篇: RedisTemplate 可沒你想的那麼簡單

相關文章
相關標籤/搜索