注:本文主要參考自《Redis設計與實現》
1、Redis兩種持久化方式
- RDB
- 執行機制:快照,直接將databases中的key-value的二進制形式存儲在了rdb文件中
- 優點:性能較高(因爲是快照,且執行頻率比aof低,而且rdb文件中直接存儲的是key-values的二進制形式,對於恢復數據也快)
- 缺點:在save配置條件之間若發生宕機,此間的數據會丟失
- AOF
- 執行機制:將對數據的每一條修改命令追加到aof文件
- 優點:數據不容易丟失
- 缺點:性能較低(每一條修改操作都要追加到aof文件,執行頻率較RDB要高,而且aof文件中存儲的是命令,對於恢復數據來講需要逐行執行命令,所以恢復慢)
2、RDB
實際中使用的配置(在redis.conf)
#發生以下三種的任何一種都會將數據庫的緩存內容寫入到rdb文件中去(寫入的方式是bgsave) #若將下述的三條命令都註釋掉,則禁止使用rdb save 900 1 #900s後至少有一個key發生了變化 save 300 10 #300s後至少有10個key發生了變化 save 60 10000 #60s後至少有10000個key發生了變化 #當後臺RDB進程導出快照(一部分的key-value)到rdb文件這個過程出錯時(即最後一次的後臺保存失敗時), #redis主進程是否還接受向數據庫寫數據 #該種方式會讓用戶知道在數據持久化到硬盤時出錯了(相當於一種監控); #如果安裝了很好的redis持久化監控,可設置爲"no" stop-writes-on-bgsave-error yes #使用LZF壓縮字符串,然後寫到rdb文件中去 #如果希望RDB進程節省一點CPU時間,設置爲no,但是可能最後的rdb文件會很大 rdbcompression yes #在redis重啓後,從rdb文件向內存寫數據之前,是否先檢測該rdb文件是否損壞(根據rdb文件中的校驗和check_sum) rdbchecksum yes #設置rdb文件名 dbfilename dump.rdb #設置rdb文件的存儲目錄 dir ./
說明:
- 具體每一項配置的詳細說明看註釋
注意:
- 對於save命令而言,配置了該命令,後臺是以bgsave來執行的
- bgsave:Redis主進程進行數據讀寫操作,RDB子進程進行數據的持久化操作,在進行持久化操作時,不阻塞主進程的讀寫操作
- 以上三條save命令只要發生任一條,bgsave命令都會發生,這就有兩個問題,假設60s內有10000個key發生了改變(寫入、刪除、更新),那麼是否會立即進行持久化呢?在這次持久化之後,假設又過了240s,而在此期間沒有任何的key的改變操作,此時是否要發生一次持久化(因爲滿足300s發生了10個key的改變,這裏是改變了10000個key)?
- 不會立即進行持久化:redis默認每隔100ms使用serverCron函數檢查一次save配置的條件是否滿足,滿足則進行bgsave,這樣的話,如果在100ms內,我已經滿足了bgsave的條件,那麼我真正執行bgsave的時候也要等到serverCron執行過來的時候
- 不會再發生持久化:redis有兩個參數dirty(記錄上一次bgsave之後的key的修改數,上邊的在240s內例子就是0)和lastsave(上一次成功執行bgsave命令的時間),配置中的每一個save配置的修改數指的就是dirty,而每一個時間段就是以lastsave爲起點計算的。
- 註釋掉所有的save命令,RDB將不起作用
- rdbcompression yes:配置成這樣是不是每一個字符串在存儲到rdb文件中時,都要進行一次壓縮操作?
- 不是:設置爲yes之後,只有當字符串的長度大於等於21個字節時,纔會進行壓縮
- rdbchecksum yes:這個校驗和存儲在哪裏?爲什麼通過比對校驗和可以判斷文件是否損壞?
- 校驗和(check_sum)存儲在RDB文件的最後八個字節中(詳細的RDB文件結構,查看《Redis這基於實現》"第10章 RDB持久化"),簡單的RDB文件結構如下:
- RDB文件開頭的前五個字節"REDIS"是判斷一個文件是不是RDB文件的標準(類似於class文件中的"魔數")
- 接下來的4個字節:RDB文件版本號(db_version)
- databases(注意是複數):這裏存放各個庫redisDb中存儲的key-value信息(是整個數據持久化和恢復的核心)
- EOF(1個字節):RDB文件正文的結束
- check_sum(8個字節):檢驗和,該值是根據前邊四部分值算出來的,在持久化的時候將該值算出來並寫入rdb文件的末尾;在根據rdb文件恢復數據的時候,再根據rdb文件中的前邊四部分值計算出一個校驗和,然後與當前rdb文件中的check_sum(即後八個字節)的內容進行比對,如果一樣,說明沒損壞,如果不一樣,說明前四部分有數據損壞(即該文件損壞)
- 校驗和(check_sum)存儲在RDB文件的最後八個字節中(詳細的RDB文件結構,查看《Redis這基於實現》"第10章 RDB持久化"),簡單的RDB文件結構如下:
- 在Redis服務器啓動時,redis會自動檢測是否有rdb文件(前提是沒有aof的時候),如果有,則根據rdb文件恢復數據,此時在恢復數據完成之前,會阻塞客戶端對redis的讀寫操作
3、AOF
實際中使用的配置(在redis.conf)
# 是否打開aof日誌功能(appendonly yes) appendonly no # aof文件的存放路徑與文件名稱 # appendfilename appendonly.aof #每一個命令,都立即同步到aof文件中去(很安全,但是速度慢,因爲每一個命令都會進行一次磁盤操作) # appendfsync always #每秒將數據寫一次到aof文件 appendfsync everysec #將寫入工作交給操作系統,由操作系統來判斷緩衝區大小,統一寫到aof文件(速度快,但是同步頻率低,容易丟數據) # appendfsync no # 在RDB持久化數據的時候,此時的aof操作是否停止,若爲yes則停止 # 在停止的這段時間內,執行的命令會寫入內存隊列,等RDB持久化完成後,統一將這些命令寫入aof文件 # 該參數的配置是考慮到RDB持久化執行的頻率低,但是執行的時間長,而AOF執行的頻率高,執行的時間短, # 若同時執行兩個子進程(RDB子進程、AOF子進程)效率會低(兩個子進程都是磁盤讀寫) # 但是若改爲yes可能造成的後果是,由於RDB持久化執行時間長,在這段時間內有很多命令寫入了內存隊列, # 最後導致隊列放不下,這樣AOF寫入到AOF文件中的命令可能就少了很多 # 在恢復數據的時候,根據aof文件恢復就會丟很多數據 # 所以,選擇no就好 no-appendfsync-on-rewrite no # AOF重寫:把內存中的數據逆化成命令,然後將這些命令重新寫入aof文件 # 重寫的目的:假設在我們在內存中對同一個key進行了100次操作,最後該key的value是100, # 那麼在aof中就會存在100條命令日誌,這樣的話,有兩個缺點: # 1)AOF文件過大,佔據硬盤空間 2)根據AOF文件恢復數據極慢(需要執行100條命令) # 如果我們將內存中的該key逆化成"set key 100",然後寫入aof文件, # 那麼aof文件的大小會大幅度減少,而且根據aof文件恢復數據很快(只需要執行1條命令) # 注意:下邊兩個約束都要滿足的條件下,纔會發生aof重寫; # 假設沒有第二個,那麼在aof的前期,只要稍微添加一些數據,就發生aof重寫 # 當aof的增長的百分比是原來的100%(即是原來大小的2倍,例如原來是100m,下一次重寫是當aof文件是200m的時候),AOF重寫 auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb #AOF重寫僅發生在當aof文件大於64m時
說明:
- 具體每一項配置的詳細說明看註釋
注意:
- 每一個客戶端命令在執行時都會直接將命令寫入AOF緩衝區
- appendfsync always:每一個命令進入緩衝區後,都會立即再從緩衝區追加到AOF文件中
- appendfsync everysec:每一秒後將緩衝區中的所有命令追加到AOF文件中
- appendfsync no:每一個命令進入緩衝區後,由操作系統來判斷什麼時候(主要是緩衝區快滿的時候)將緩衝區中的所有命令追加到AOF文件中
- aof重寫需要滿足配置文件中的兩個條件
- aof重寫採用後臺子進程執行
- aof的重寫會進行大量的寫入操作,如果用單線程來做這個事兒,就會長時間阻塞主進程(redis是單線程),這個時候客戶端的讀寫就會失效
- 採用aof後臺重寫進程造成的問題:
- 如果在後臺進程重寫期間,有新的命令對數據庫進行了讀寫,新的aof文件就與數據庫的存儲內容不同了。(注意:在後臺進程重寫期間,對數據庫的讀寫操作還會進入aof緩衝區,還是會執行aof文件的命令寫入,但需要注意的是,雖然這個期間所有的命令還是寫入aof文件了,但是這個aof文件會被重寫後的新的aof文件所替換,新的aof文件可沒有這些命令,那麼如果在下次重寫發生之前發生宕機,採用aof恢復數據庫的時候,那就丟失了很多命令了)
- 解決方案:設置一個aof重寫緩衝區,僅僅用於在後臺進程重寫期間,將發生的數據庫讀寫命令寫入到重寫緩衝區中(當然,此時的服務器進程除了將發生的數據庫讀寫命令寫入到重寫緩衝區中,還會寫入到aof緩衝區,來保證正常的aof操作),之後當重寫子進程完成重寫後,向服務器主進程發送一個信號,此時服務器主進程將aof重寫緩衝區中的命令追加到新的aof文件中去,用新的aof文件替換掉舊的aof文件。
- 注意:
- 在後臺進程重寫期間,將發生的數據庫讀寫命令寫入到重寫緩衝區中時,爲什麼還要將這些命令寫入到aof緩衝區(因爲這些命令會寫入舊的aof文件)?
- 如果能百分之百保證在最後的主進程用新的aof文件替換了舊的aof文件(就是在這之前不宕機),那麼寫入到aof緩衝區沒用(因爲會被覆蓋),但是如果在這之前宕機,那麼我們的aof文件還是舊的aof文件,這時候命令寫入到aof緩衝區就可以保證該aof文件儘可能多的保存命令,將來用於恢復數據庫最多丟失的也就是1s的數據。(appendfsync everysec)
- 在"服務器主進程將aof重寫緩衝區中的命令追加到新的aof文件中去"這段操作期間,如果有新的客戶端讀寫命令,都將被阻塞(因爲是主進程在做上述操作)。這也是整個aof重寫過程中唯一被阻塞的部分。
- 在後臺進程重寫期間,將發生的數據庫讀寫命令寫入到重寫緩衝區中時,爲什麼還要將這些命令寫入到aof緩衝區(因爲這些命令會寫入舊的aof文件)?
總結:
- 如果既配置了RDB,又配置了AOF,則在進行數據持久化的時候,都會進行,但是在根據文件恢復數據的時候,以AOF文件爲準,RDB文件作廢
- 需要注意:數據的恢復是阻塞操作(此間所到來的任何客戶端讀寫請求都失效)
- bgsave和bgrewriteaof(後臺aof重寫)這兩個命令不可以同時發生
- 如果bgsave在執行,此間到來的bgrewriteaof在bgsave執行之後,再執行
- 如果bgrewriteaof在執行,此間到來的bgsave丟棄
- RDB和AOF可以同時配置,但是最後還原數據庫的時候是以aof文件來還原的