持久化存儲是將 Redis 存儲在記憶體中的數據存儲在硬碟中,實現數據的永久保存。我們都知道 Redis 是一個基於記憶體的 nosql 資料庫,記憶體存儲很容易造成數據的丟失,因為當伺服器關機等一些異常情況都會導致存儲在記憶體中的數據丟失。 ...
> 為什麼要做持久化存儲?
持久化存儲是將 Redis 存儲在記憶體中的數據存儲在硬碟中,實現數據的永久保存。我們都知道 Redis 是一個基於記憶體的 nosql 資料庫,記憶體存儲很容易造成數據的丟失,因為當伺服器關機等一些異常情況都會導致存儲在記憶體中的數據丟失。
> 持久化存儲分類
在 Redis 中,持久化存儲分為兩種。一種是 aof 日誌追加的方式,另外一種是 rdb 數據快照的方式。
> RDB持久化存儲
什麼是RDB持久化存儲
RDB持久化存儲即是將redis存在記憶體中的數據以快照的形式保存在本地磁碟中。
.RDB持久化存儲分為自動備份和手動備份
1.手動備份通過 save 命令和 bgsave 命令。save是同步阻塞,而 bgsave 是非阻塞(阻塞實際發生在 fork 的子進程中)。因此,在我們實際過程中大多是使用bgsave命令實現備份.
```shell
redis> SAVE
OK
```
```shell
redis> BGSAVE
Background saving started
```
2.自動備份
a.修改配置項 save m n即表示在 m 秒內執行了 n 次命令則進行備份.
b.當Redis 從伺服器項主伺服器發送複製請求時,主伺服器則會使用 bgsave命令生成 rbd 文件,然後傳輸給從伺服器.
c.當執行 debug reload 命令時也會使用 save 命令生成rdb文件.
d.當使用 shutdown 命令關掉服務時,如果沒有啟用 aof方式實現持久化則會採用bgsave的方式做持久化.同時shutdown後面可以加備份參數[nosave|save].
bgsave持久化存儲實現原理
![圖片描述](//img.mukewang.com/5c8518900001734506990490.png)
1.執行bgsave命令,Redis父進程判斷當前是否存在正在執行的子進程,如果存在則直接返回.
2.父進程fork一個子進程(fork的過程中會造成阻塞的情況),這個過程可以使用info stats命令查看latest_fork_usec選項,查看最近一次fork操作小號的時間,單位是微妙.
3.父進程fork完之後,則會返回Background saving started信息提示,此時fork阻塞解除.
4.fork出的子進程開始根據父進程記憶體數據生成臨時的快照文件,然後替換原文件.使用lastsave命令可以查看最後一次生成rdb的時間,對應info的rdb_last_save_time選項.
5.當備份完畢之後向父進程發送完成信息,具體可以見info Persistence下的rbd_*選項.
RDB持久化的優勢與劣勢
優勢:
1.文件實現的數據快照,全量備份,便於數據的傳輸.比如我們需要把A伺服器上的備份文件傳輸到B伺服器上面,直接將rdb文件拷貝即可.
2.文件採用壓縮的二進位文件,當重啟服務時載入數據文件,比aof方式更快.
劣勢:
1.rbd採用加密的二進位格式存儲文件,由於Redis各個版本之間的相容性問題也導致rdb由版本相容問題導致無法再其他的Redis版本中使用.
2.時效性差,容易造成數據的不完整性.因為rdb並不是實時備份,當某個時間段Redis服務出現異常,記憶體數據丟失,這段時間的數據是無法恢復的,因此易導致數據的丟失.
RDB文件常見的處理方式
1.當遇到磁碟寫滿情況,可以使用如下命令來切換存儲磁碟
```shell
// dirName則是新的存儲目錄名(該方式同樣適用於aof格式)
config set dir dirName
```
2.文件壓縮處理,雖然對CPU具有消耗,但是減少體積的暫用,同時做文件傳輸(主從複製)也減少消耗.
```shell
// 修改壓縮開啟或關閉
config set rdbcompression yes|no
```
3.rbd備份文件損壞檢測.可以使用redis-check-rdb工具檢測rdb文件,該工具預設在/usr/local/bin/目錄下麵.
```shell
[root@syncd redis-data]# /usr/local/bin/redis-check-rdb ./6379-rdb.rdb
[offset 0] Checking RDB file ./6379-rdb.rdb
[offset 26] AUX FIELD redis-ver = '5.0.3'
[offset 40] AUX FIELD redis-bits = '64'
[offset 52] AUX FIELD ctime = '1552061947'
[offset 67] AUX FIELD used-mem = '852984'
[offset 83] AUX FIELD aof-preamble = '0'
[offset 85] Selecting DB ID 0
[offset 105] Checksum OK
[offset 105] \o/ RDB looks OK! \o/
[info] 1 keys read
[info] 0 expires
[info] 0 already expired
```
> AOF持久化存儲
AOF持久化存儲是什麼
AOF持久化存儲便是以日誌的形式將redis存儲在aof_buf緩衝區中的數據寫入到磁碟中。簡而言之,就是記錄redis的操作日誌,將redis執行過的命令記錄下載,當我們需要數據恢復時,redis去重新執行一次日誌文件中的命令.
如何配置持久化存儲
```shell
// 將no改為yes,控制aof開啟與否
appendonly no
// 控制aof文件名稱,存儲的目錄便是dir配置項
appendfilename "appendonly.aof"
// 三種備份策略(三者只需要開啟以一個即可)
# appendfsync always // 命令寫入立即寫入磁碟
appendfsync everysec // 每秒實現文件的同步,寫入磁碟
# appendfsync no // 隨機進行文件的同步,同步操作則交給操作系統來負責,通常時間是最長30s
```
AOF持久化存儲實現原理
aof日誌追加方式實現持久化存儲,需要經歷如下四個過程.命令寫入->文件同步->文件重寫->文件重載
![圖片描述](//img.mukewang.com/5c8518cc00015eed06320525.png)
1.redis命令寫入,此時會將redis命令寫入aof_buf換從區.
2.緩衝區中數據根據備份策略實現寫入日誌文件.
3.當aof的文件越來越龐大,會根據我們的配置策略來實現aof的重寫,實現文件的壓縮,減少體積.
4.當redis重新啟動時,在去重寫載入aof文件,達到數據恢復的目的.
命令寫入
命令寫入主要是將文件執行過的命令寫入到日誌文件中.並且日誌文件尊徐文本協議格式,下麵示例代碼便是aof日誌文件中存儲的內容格式.
```shell
*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n
```
aof採用的是文本協議格式。主要是原因根據資料提示,可以能使由於如下原因.
1.文本協議的相容性好.前面我們提及到了rdb文件是進行二進位加密,可能不同版本之間會出現不相容的情況,採用文本協議可以加避免該問題。同時文本協議也可以減少跨平臺使用所帶來的諸多問題.
2.可讀性強.由於aof是將命令寫入文件中,我們可以直接查看命令內容,同時也可以修改日誌文件內容.
3.開啟aof後,所有的文件文件都包含追加操作,直接採用文本協議,減少二次開銷(這一點,個人不是很理解.因為我們的aof是保存的是命令,當我們再次去載入的時候,會去執行一次裡面的命令,當文件大的時候應該是比較耗時的吧。如果沒有做好文件重寫策略,大量重覆無效的命令執行,對於二進位加密的rdb格式,不需要再去轉換,這一點確實可以減少二次開銷).
文件寫入
文件寫入是將aof_buf緩衝區的命令寫入到文件中.文件寫入的策略有如下三種方式
|配置項 | 配置說明 |
|:---: | :---: |
|always | 命令寫入到aof_buf緩衝區中之後立即調用系統的<font color='red'>fsync操作</font>同步到aof文件中,fsync完成後線程返回. |
|everysec | 命令寫入到aof_buf緩衝區後<font color='red'>每隔一秒</font>調用系統的<font color='red'>write操作</font>,write完成後線程返回. |
|no | 命令寫入aof_bug緩衝區後調用系統write操作,不對aof文件做fsync同步,同步硬碟操作由<font color='red'>系統操作</font>完成,時間一般最長為30s. |
系統調用write和fsync說明:
·write操作會觸發延遲寫( delayed write) 機制。 Linux在內核提供頁緩衝區用來提高硬碟IO性能。 write操作在寫入系統緩衝區後直接返回。 同步硬碟操作依賴於系統調度機制, 例如: 緩衝區頁空間寫滿或達到特定時間周期。 同步文件之前, 如果此時系統故障宕機, 緩衝區內數據將丟失.
·fsync針對單個文件操作( 比如AOF文件) , 做強制硬碟同步, fsync將阻塞直到寫入硬碟完成後返回, 保證了數據持久化.
文件寫入策略分析
配置為always時, 每次寫入都要同步AOF文件, 在一般的SATA硬碟上, Redis只能支持大約幾百TPS寫入, 顯然跟Redis高性能特性背道而馳,
不建議配置.
配置為no。由於操作系統每次同步AOF文件的周期不可控, 而且會加大每次同步硬碟的數據量, 雖然提升了性能, 但數據安全性無法保證.
配置為everysec。是建議的同步策略, 也是預設配置, 做到兼顧性能和數據安全性。 理論上只有在系統突然宕機的情況下丟失1秒的數據.
文件重載
1.為什麼要文件做文件重載操作?
由於aof採用的是日誌追加,我們redis命令不斷的寫入,aof文件的體積也也會不斷的增加.因此redis引入了aof重寫機制達到減小aof文件體積.<font color="blue">aof文件重寫是把redis進程內的數據轉換為寫命令同步到新的aof文件的過程(這一點其實不是特別明白,文件重寫不是針對aof文件文件做操作的嗎?為什麼這裡是將redis進程內的數據轉換為命令寫入文件,這裡的進程內的數據不是太明白,還有待深入研究.個人理解的就是將舊的aof文件內容根據重寫策略,進行優化生成新的aof文件。).</font>
2.文件重載有什麼好處?
文件重載主要優化的地方有如下三點。使用文件重載既可以減少文件的體積,同時去掉了一些無效的操作,可以加快文件重載效率.
a.將一些在進程內無效的數據不在寫入新的文件.如過期的鍵.
b.去掉一些無效的命令.如del key1.
c.簡化操作.如lpush list a,lpush list b.直接可以簡化為lpush list a b.
3.文件重載由那些方式?
文件重載有自動觸發機制和手動觸發機制.
手動觸發機制:直接使用bgrewriteaof命令即可.該命令在fork子進程的時候會發生阻塞.
自動觸發機制:
auto-aof-rewrite-min-size:aof重寫時文件最小的體積,預設的是64M.
auto-aof-rewrite-percentage:代表當前AOF文件空間( aof_current_size) 和上一次重寫後AOF文件空間( aof_base_size) 的比值.
```php
自動觸發時機=aof_current_size>auto-aof-rewrite-minsize&&( aof_current_size-aof_base_size) /aof_base_size>=auto-aof-rewritepercentage
```
其中aof_current_size和aof_base_size可以在info Persistence統計信息中查看.
4.文件重載實現的原理是怎樣的?
![圖片描述](//img.mukewang.com/5c8518f400019ab607170711.png)
1.執行重寫命令,判斷是否存在子進程。
如果已經有子進程在進行aof重寫,則會提示如下信息.
```shell
ERR Background append only file rewriting already in progress
```
如果已經存在子進程在進行bgsave操作,重寫命令會延遲到bgsave命令完成之後進行,會返回如下信息.
```shell
Background append only file rewriting scheduled
```
2.父進程會fork一個子進程,在fork子進程的過程中會造成阻塞.
3.fork子進程結束阻塞解除,進行其他新的命令操作.新的命令依舊根據文件寫入策略同步數據,保證aof機制正確進行(圖中3.1).
4.子進程在進行寫的過程中,由於fork操作運用的是寫時複製技術,子進程只能共用fork操作時記憶體保留的數據,新的數據是無法操作的.父進程在這過程中仍然在響應其他的命令,於是Redis會使用aof重寫緩存區來保存這部分新的數據(圖中3.2).
5.子進程進行根據重寫規則將數據寫入到新的aof文件中,並且每次寫入有大小限制,通過aof-rewrite-incremental-fsync配置項來控制,預設是32M,這樣可以見減少單次刷盤(I/O寫)造成硬碟阻塞.
6.子進程在完成重寫之後,會向父進程發送信息,父進程更新統計信息.可參看info persistence下的aof_*相關統計。
7.父進程會把新寫入存在aof重寫緩衝區的數據寫入到aof文件中(圖5.2).
8.將新的aof文件替換掉舊的aof文件.
<font color='blue'>在第3和4中,其實不是特別理解.不理解的是為什麼父進程在響應新的命令會寫入舊的aof文件,還要aof重寫緩存區.個人理解的是,父進程在進行新命令寫入處理的策略是,按照正常的備份策略寫入舊的aof的同時也把新的命令寫入重寫緩衝區,在第5.2中將這部分新的數據寫入到新的aof文件中,這樣保證數據的完整性.</font>
文件重載
文件重載就是將文件重新加入到redis服務中.比如redis服務重啟用於數據恢復.redis的重載機制非常完善,具體流程如下.![圖片描述](//img.mukewang.com/5c85191d0001b48e07060788.png)
AOF文件常見的問題處理
1.文件損壞
我們在載入損壞的文件是可能提示如下信息.
```shell
Bad file format reading the append only file: make a backup of your AOF file,then use ./redis-check-aof --fix <filename>
```
此時我們可以使用redis-check-aof --fix命令進行修複(記得對文件做個備份).修複後使用diff-u進行數據對比,找出部分丟失的數據.
2.文件載入不完整
這可能是數據在備份的時候,redis服務異常,導致備份不完整.可以使用redis的aof-load-truncated相容該異常
AOF的優缺點
優點:
多種文件寫入(fsync)策略.
數據實時保存,數據完整性強.即使丟失某些數據,制定好策略最多也是一秒內的數據丟失.
可讀性強,由於使用的是文本協議格式來存儲的數據,可有直接查看操作的命令,同時也可以手動改寫命令.
缺點:
文件體積過大,載入速度比rbd慢.由於aof記錄的是redis操作的日誌,一些無效的,可簡化的操作也會被記錄下來,造成aof文件過大.但該方式可以通過文件重寫策略進行優化.
選擇AOF還是RDB進行數據的持久化
1.針對不同的情況來選擇,建議使用兩種方式相結合.
2.針對數據安全性、完整性要求高的採用aof方式.
3.針對不太重要的數據可以使用rdb方式.
4.對於數據進行全量備份,便於數據備份的可以採用rdb方式.
原文轉自微信公眾號:浪子編程走四方