? ? ? ? 在上一篇文章中對Redis的兩種持久化方式進行了介紹,還介紹了各自的優(yōu)缺點以及如何選擇,這篇文章就介紹一下redis的持久化應該怎么配置,數(shù)據(jù)恢復應該怎么操作。
1.RDB配置和數(shù)據(jù)恢復流程
(1).RDB的配置
????????首先找到redis的配置文件,我的redis裝在了centos上,配置文件的目錄為:/etc/redis/6379.conf,打開它找到snapshotting。

????????可以在snapshotting下配置多個save,這個save其實就是檢查點,redis可以配置多個檢查點,每到一個檢查點,就會去check一下,是否有指定的key數(shù)量發(fā)生了變更,如果有,就生成一個新的dump.rdb文件。當然,我們也可以通過手動調(diào)用save或者bgsave命令,同步或異步執(zhí)行rdb快照生成。
save 900 1:表示每隔900秒如果有1個key發(fā)生變化就會生成一個新的rdb文件
save 300 10:表示每隔300秒如果有10個key發(fā)生變化就會生成一個新的rdb文件
save 60 10000:表示每隔60秒如果有10000個key發(fā)生變化就會生成一個新的rdb文件
? ? ? ? save 5 1 是我們?yōu)榱藢嶒灧奖阍O(shè)置的一個檢查點
(2).RDB持久化機制的工作流程
(a).redis根據(jù)配置自己嘗試去生成rdb快照文件
(b).fork一個子進程出來
(c).子進程嘗試將數(shù)據(jù)dump到臨時的rdb快照文件中
(d).完成rdb快照文件的生成之后,就替換之前的舊的快照文件
同時只有一個dump.rdb,每次生成一個新的快照,都會覆蓋之前的老快照。
(3).rdb數(shù)據(jù)恢復實驗
????????首先我們打開redis-cli存儲一些數(shù)據(jù)(ps:此時我們還沒有在redis.conf中添加save 5 1):

????????現(xiàn)在我們把一些數(shù)據(jù)都已經(jīng)放在了redis里面,然后我們shut down redis,并且重啟redis:

????????那么現(xiàn)在就有點奇怪了,我們還沒有添加檢查點 save 5 1,為什么數(shù)據(jù)還是在redis中?其實在我們shutdown redis-cli的時候就已經(jīng)自動生成了一次快照文件了,所以上述操作是安全操作!(那你說個屁!);
????????好的,我們接下來就不shutdown 而是直接kill -9 來粗暴殺死redis的進程并且刪除掉/var/run 下面redis_6379.pid,模擬redis的故障場景:

????????這時就發(fā)現(xiàn)我們剛剛添加的三個值都拿不到了。
????????然后我們自己手動設(shè)置一個檢查點:save 5 1到redis的配置文件中。然后再次將剛剛?cè)齻€值保存到redis中,然后等待五秒,重復上面殺死redis進程的操作,然后在重啟redis,現(xiàn)在我們就發(fā)現(xiàn)之前的值沒有因為redis被殺死而丟失。

????????好的,以上就是我們使用RDB進行數(shù)據(jù)恢復的實驗,save 5 1這個檢查點記得注釋掉或者刪除,我們沒有必要這么頻繁的去生成快照。
2.AOF配置和數(shù)據(jù)恢復流程
(1).AOF的配置
????????我們在redis的配置文件中搜索appendonly就可以找到aof的相關(guān)配置了。
????????其實redis默認的持久化方式時rdb,rdb是默認打開的,我們要使用aof的話就需要單獨打開。

????????在生產(chǎn)中,aof一般都是要打開的,除非你覺得丟失幾分鐘的數(shù)據(jù)無大礙。
????????設(shè)置 appendonly yes 后就打開了aof,打開aof之后,redis每接受到一條命令都會先寫入到日志文件中,當然不會直接寫到aof文件中,他會先寫入到ox cache中,然后每隔一段時間調(diào)用操作系統(tǒng)的fsync操作,將os cache中的數(shù)據(jù)同步到磁盤上的aof文件中。
????????而且,當rdb和aof同時開啟的時候,redis也會優(yōu)先aof來進行數(shù)據(jù)恢復,因為aof的數(shù)據(jù)相對來說比rdb完整。
aof有三種策略供君選擇:
appendfsync always:?
????????每次寫入一條數(shù)據(jù),立即將這個數(shù)據(jù)對應的寫日志fsync到磁盤上去,性能非常非常差,吞吐量很低; 如果說你要求redis里的數(shù)據(jù)一條都不丟,那就選擇此策略。
appendfsync everysec:
????????每秒將os cache中的數(shù)據(jù)fsync到磁盤,這個最常用的,生產(chǎn)環(huán)境一般都這么配置,性能很高,QPS還是可以上萬的。
appendfsync no:
? ? ? ?redis?僅僅負責將數(shù)據(jù)寫入os cache就不管了,然后os自己會時不時根據(jù)自己的策略將數(shù)據(jù)刷入磁盤,人為不可控。
(2).aof數(shù)據(jù)恢復實驗
????????首先我們打開aof的開關(guān),啟用aof,然后向redis中添加數(shù)據(jù),然后找到aof文件(我的持久化文件放在:/var/redis/6379),aof中按順序存放指令,如果你知道aof的規(guī)則的話是可以讀懂的,也就是說,aof文件是可讀的。

????????我使用的策略是everysec,也就是每秒調(diào)用一次fsync,redis先將數(shù)據(jù)寫入os cache中,一秒后才調(diào)用fsync操作將數(shù)據(jù)寫入磁盤中,數(shù)據(jù)沒有寫入磁盤的話是不安全的,也就是說,everysec還是有可能丟失一秒的數(shù)據(jù)。
????????我們將redis的進程殺死,看看數(shù)據(jù)恢復成功了沒:

? ? ? ? 數(shù)據(jù)成功的被恢復了,redis進程啟動的時候,直接就會從appendonly.aof中加載所有的日志,把內(nèi)存中的數(shù)據(jù)恢復回來。
(3).aof的rewrite機制
redis中的數(shù)據(jù)其實有限的,很多數(shù)據(jù)可能會自動過期,可能會被用戶刪除,可能會被redis用緩存清除的算法清理掉。redis中的數(shù)據(jù)會不斷淘汰掉舊的,就一部分常用的數(shù)據(jù)會被自動保留在redis內(nèi)存中。所以可能很多之前的已經(jīng)被清理掉的數(shù)據(jù),對應的寫日志還停留在AOF中,AOF日志文件就一個,會不斷的膨脹,變得很大很大。所以AOF會自動在后臺每隔一定時間做rewrite操作,比如日志里已經(jīng)存放了針對100w數(shù)據(jù)的寫日志了; redis內(nèi)存只剩下10萬; 基于內(nèi)存中當前的10萬數(shù)據(jù)構(gòu)建一套最新的日志,到AOF中; 覆蓋之前的老日志; 確保AOF日志文件不會過大,保持跟redis內(nèi)存數(shù)據(jù)量一致。
首先,我們先去配置文件中搜索一下rewrite,可以看看相關(guān)的說明。

????????rewrite的策略我們使用默認的就好,有興趣的讀者可以去研究一下redis的rewrite策略,我在這里就講一下上圖對應的兩個參數(shù),這兩個參數(shù)相對來說比較重要。這兩個參數(shù)怎么解釋呢?比如說上一次AOF rewrite之后,是128mb。然后就會接著128mb繼續(xù)寫AOF的日志,如果發(fā)現(xiàn)增長的比例,超過了之前的100%,256mb,就可能會去觸發(fā)一次rewrite但是此時還要去跟min-size,64mb去比較,256mb > 64mb,才會去觸發(fā)rewrite。
(4).aof rewrite過程詳解
(a).redis fork一個子進程
(b).子進程基于當前內(nèi)存中的數(shù)據(jù),構(gòu)建日志,開始往一個新的臨時的AOF文件中寫入日志
(c).redis主進程,接收到client新的寫操作之后,在內(nèi)存中寫入日志,同時新的日志也繼續(xù)寫入舊的AOF文件
(d).子進程寫完新的日志文件之后,redis主進程將內(nèi)存中的新日志再次追加到新的AOF文件中
(e).用新的日志文件替換掉舊的日志文件

(5).aof文件破損修復
????????當redis在append數(shù)據(jù)到aof文件中的時候,服務(wù)器宕機了,那么就可能造成aof文件的損壞,redis提供了相關(guān)的工具來修復破損的aof文件。使用redis-check-aof --fix命令來修復破損的AOF文件。
????????我們來做一個小實驗,我們把備份的aof文件copy出來一份,然后更改它,是他破損,然后使用redis的修復工具來修復它。
? ? ? ? 首先將aof文件copy出來

我們編輯這個文件,然后更改這個文件:

然后使用redis的修復工具修復這個文件:

redis修復工具就會把最后那行多余的指令刪除掉。
3.RDB和AOF同時工作
? ? ? ? 因為redis的持久化操作是非常消耗性能的,那么當他們倆發(fā)生沖突的時候會怎么辦?
(1)如果RDB在執(zhí)行snapshotting操作,那么redis不會執(zhí)行AOF rewrite; 如果redis再執(zhí)行AOF rewrite,那么就不會執(zhí)行RDB snapshotting
(2)如果RDB在執(zhí)行snapshotting,此時用戶執(zhí)行BGREWRITEAOF命令,那么等RDB快照生成之后,才會去執(zhí)行AOF rewrite
(3)同時有RDB snapshot文件和AOF日志文件,那么redis重啟的時候,會優(yōu)先使用AOF進行數(shù)據(jù)恢復,因為其中的日志更完整
(4)在有rdb的dump和aof的appendonly的同時,rdb里也有部分數(shù)據(jù),aof里也有部分數(shù)據(jù),這個時候其實會發(fā)現(xiàn),rdb的數(shù)據(jù)不會恢復到內(nèi)存中
大家也可做個小實驗,將aof中的數(shù)據(jù)刪除一條(這條數(shù)據(jù)已經(jīng)持久化到rdb中),然后用redis的修復工具進行修復,在用這個文件替換aof文件,重啟redis,最后會發(fā)現(xiàn)rdb中的數(shù)據(jù)不會被恢復到內(nèi)存中。
redis的數(shù)據(jù)恢復完全是依賴于底層的磁盤的持久化的,如果rdb和aof上都沒有數(shù)據(jù),那就真的沒了。
4.在實際項目的使用中,我們應該如何配置和使用呢?
(1).配置策略
????????在企業(yè)級的項目中,我們使用rdb的默認配置也無大礙,即:
save 900 1
save 300 10
save 60 10000
????????唯一可能要調(diào)整的就是 save 60 10000 這個檢查點,因為rdb的生成還是比較耗費資源的,如果說低峰期數(shù)據(jù)量很小,那么也沒太大必要。但是如果你要保證RDB最多丟一分鐘的數(shù)據(jù),那么剩下的就是你自己根據(jù)實際情況去配置,到底是一分鐘-10000生成rdb,還是說設(shè)置成1分鐘-1000生成rdb。
????aof的話這里推薦把他打開,調(diào)用fsync的策略推薦使用everysec,然后根據(jù)你項目的實際情況去設(shè)置以下兩個屬性:
auto-aof-rewrite-percentage 100 這個屬性改的必要也不是特別的大
auto-aof-rewrite-min-size 64mb 這個根據(jù)你的數(shù)據(jù)量來定
(2).數(shù)據(jù)備份方案
RDB的話是非常適合做冷備的,每次RDB生成完成之后就不會再修改了,備份的方案可以這樣:
(a).寫crontab定時調(diào)度腳本去做數(shù)據(jù)備份
(b).每小時都copy一份rdb的備份,到一個目錄中去,僅僅保留最近48小時的備份
(c).每天都保留一份當日的rdb的備份,到一個目錄中去,僅僅保留最近1個月的備份
(d).每次copy備份的時候,都把太舊的備份給刪了
(e).每天晚上將當前服務(wù)器上所有的數(shù)據(jù)備份,發(fā)送一份到遠程的云服務(wù)上去
每小時copy一次備份,刪除48小時前的數(shù)據(jù)
crontab -e
0 * * * * sh /usr/local/redis/copy/redis_rdb_copy_hourly.sh
redis_rdb_copy_hourly.sh
```
#!/bin/sh
cur_date=`date +%Y%m%d%k`
rm -rf /usr/local/redis/snapshotting/$cur_date
mkdir /usr/local/redis/snapshotting/$cur_date
cp /var/redis/6379/dump.rdb /usr/local/redis/snapshotting/$cur_date
del_date=`date -d -48hour +%Y%m%d%k`
rm -rf /usr/local/redis/snapshotting/$del_date
```
每天copy一次備份
crontab -e
0 0 * * * sh /usr/local/redis/copy/redis_rdb_copy_daily.sh
redis_rdb_copy_daily.sh
```
#!/bin/sh
cur_date=`date +%Y%m%d`
rm -rf /usr/local/redis/snapshotting/$cur_date
mkdir /usr/local/redis/snapshotting/$cur_date
cp /var/redis/6379/dump.rdb /usr/local/redis/snapshotting/$cur_date
del_date=`date -d -1month +%Y%m%d`
rm -rf /usr/local/redis/snapshotting/$del_date
```
每天一次將所有數(shù)據(jù)上傳一次到遠程的云服務(wù)器上去
(3).數(shù)據(jù)恢復方案
(a).如果是redis進程掛掉,那么重啟redis進程即可,直接基于AOF日志文件恢復數(shù)據(jù)。
(b).如果是redis進程所在機器掛掉,那么重啟機器后,嘗試重啟redis進程,嘗試直接基于AOF日志文件進行數(shù)據(jù)恢復。AOF沒有破損,也是可以直接基于AOF恢復的,如果AOF文件出現(xiàn)了破損,則用redis的修復工具修復之后在進行恢復。
(c).如果redis當前最新的AOF和RDB文件出現(xiàn)了丟失/損壞,那么可以嘗試基于該機器上當前的某個最新的RDB數(shù)據(jù)副本進行數(shù)據(jù)恢復。
(d).如果當前機器上的所有RDB文件全部損壞,那么從遠程的云服務(wù)上拉取最新的RDB快照回來恢復數(shù)據(jù)。
(e).如果是發(fā)現(xiàn)有重大的數(shù)據(jù)錯誤,比如某個小時上線的程序一下子將數(shù)據(jù)全部污染了,數(shù)據(jù)全錯了,那么可以選擇某個更早的時間點,對數(shù)據(jù)進行恢復。
(4).基于RDB冷備恢復數(shù)據(jù)
????????基于rdb冷備其實是有一些小坑的,這里拿出來記錄一下,如果你同時打開RDB和AOF,如果你使用的是AOF的冷備去恢復數(shù)據(jù),那么很簡單,將redis的aof文件替換成你的冷備然后重啟redis就行了。
????????但是要是你使用的是RDB的冷備,你首先要把redis現(xiàn)有的aof文件刪掉,然后把rdb文件替換成你的冷備,但是由于你開著aof,所以在aof啟動的時候會直接生成一份空的aof文件,而你的RDB冷備會被無視掉,過了一段時間可能會被重寫掉。你可能會想,那我們把AOF關(guān)掉不就行了嗎?我們把AOF關(guān)掉,重啟redis,確實,RDB的數(shù)據(jù)會被恢復到redis中,現(xiàn)在數(shù)據(jù)回來了,但是AOF不能關(guān)掉,所以你再次修改配置文件打開AOF重啟redis,這時你發(fā)現(xiàn)辛辛苦苦恢復的數(shù)據(jù)又沒了,其實還是因為打開AOF重新生成了一份空的AOF文件,RDB里面就算有數(shù)據(jù)也不會恢復到redis當中。那么怎么解決呢?我們可以通過熱修改redis屬性的方式進行恢復,我們先把redis的AOF關(guān)掉,用RDB冷備恢復數(shù)據(jù),在redis-cli 中使用 config set 指令來開啟AOF,等RDB冷備的數(shù)據(jù)全被寫入AOF后,再修改配置文件打開AOF,重啟redis即可。以下是具體的流程:
(a).停止redis
(b).關(guān)閉aof
(c).拷貝rdb備份
(d).重啟redis,確認數(shù)據(jù)恢復
(e).直接在命令行熱修改redis配置,打開aof,redis就會將內(nèi)存中的數(shù)據(jù)對應的日志,寫入aof文件中
(f).此時aof和rdb兩份數(shù)據(jù)文件的數(shù)據(jù)就同步了