你說你精通 Redis,你看過持久化的配置嗎?
我們之前介紹了 Redis
五種數(shù)據(jù)類型的命令 與 配置文件的基本配置 ,今天讓我們從理論和配置兩個(gè)層面來揭開 Redis
持久化的神秘面紗。
所謂持久化可以簡(jiǎn)單理解為將內(nèi)存中的數(shù)據(jù)保存到硬盤上存儲(chǔ)的過程。持久化之后的數(shù)據(jù)在系統(tǒng)重啟或者宕機(jī)之后依然可以進(jìn)行訪問,保證了數(shù)據(jù)的安全性。
Redis
有兩種持久化方案,一種是快照方式( SNAPSHOTTING
),簡(jiǎn)稱 RDB
;一種是只追加模式( APPEND ONLY MODE
),稱為AOF。接下來讓我們分別了解一下它們的使用與注意事項(xiàng)。
RDB
RDB
為 Redis DataBase
的縮寫,是 Redis
默認(rèn)的持久化方案。它能夠在指定的時(shí)間間隔內(nèi)將內(nèi)存數(shù)據(jù)集快照( snapshot
)寫入磁盤,恢復(fù)時(shí)將快照文件( dump.rdb
)讀回內(nèi)存。
我們先來扒一下配置文件中的 SNAPSHOTTING
:
配置文件
save <seconds> <changes>
在給定的 秒數(shù) 內(nèi),如果對(duì)數(shù)據(jù)庫執(zhí)行的 寫入操作數(shù) 達(dá)到設(shè)定的值,則將數(shù)據(jù)同步到數(shù)據(jù)文件。支持多個(gè)條件配合, Redis
默認(rèn)配置文件中提供了三個(gè)條件:
- save 900 1 //900s內(nèi)有1個(gè)更改
- save 300 10 //300s內(nèi)有10個(gè)更改
- save 60 10000 //60s內(nèi)有10000次更改
注意:若不想用 RDB
方案,可以把 save ""
的注釋打開,上邊三個(gè)注釋掉。
stop-writes-on-bgsave-error yes
當(dāng) bgsave
出現(xiàn)錯(cuò)誤時(shí), Redis
是否停止執(zhí)行寫命令;
- 如果為
yes
,則當(dāng)硬盤出現(xiàn)問題時(shí),Redis
將停止接受寫入操作,這樣我們可以及時(shí)發(fā)現(xiàn),避免數(shù)據(jù)的大量丟失;
- no
- Redis
- bgsave
如果已經(jīng)設(shè)置了對(duì) Redis
服務(wù)器的正確監(jiān)視和持久性,即采用了其他手段發(fā)現(xiàn)和控制數(shù)據(jù)完整性,可能希望禁用此功能,以便即使在磁盤、權(quán)限等方面出現(xiàn)問題時(shí), Redis
仍能正常工作。
注意:如果后臺(tái)保存過程將再次開始工作, Redis
將自動(dòng)允許再次寫入。
rdbcompression yes
指定存儲(chǔ)到本地?cái)?shù)據(jù)庫時(shí)是否 壓縮 ( Redis
采用 LZF
壓縮)數(shù)據(jù),默認(rèn)為 yes
。如果為了節(jié)省 CPU
時(shí)間,可以關(guān)閉該選項(xiàng),但會(huì)導(dǎo)致數(shù)據(jù)庫文件變得巨大。
rdbchecksum yes
從 RDB
版本 5
開始,在存儲(chǔ)快照后,還可以使用 CRC64
算法來進(jìn)行數(shù)據(jù)校驗(yàn), CRC64
校驗(yàn)放在文件的末尾。開啟之后,保存和加載 RDB
文件時(shí)會(huì)增加大約 10%
的性能消耗,如果希望獲取到最大的性能提升,可以關(guān)閉此功能。
禁用 校驗(yàn)和 創(chuàng)建的 RDB
文件的校驗(yàn)和為零,這將告訴加載代碼跳過檢查。
dbfilename dump.rdb
指定本地?cái)?shù)據(jù)庫文件名,重啟之后自動(dòng)加載進(jìn) 內(nèi)存 ,手動(dòng)執(zhí)行 save
命令的話即刻生效。
大坑請(qǐng)注意: flushall
、 shutdown
命令都會(huì)清空并提交至 dump.rdb
dir ./
指定本地?cái)?shù)據(jù)庫存放目錄。
理論
工作方式
- Redis
- dump.rdb
- fork()
RDB
中;
- RDB
- Redis
- RDB
- RDB
- RDB
這種工作方式使得 Redis
可以從寫時(shí)復(fù)制( copy-on-write
)機(jī)制中獲益。
如何觸發(fā)RDB快照
-
配置文件中默認(rèn)的快照配置;
- 命令
save
(阻塞, 只管保存快照,其他的等待)或者是bgsave
(異步)命令,快照同時(shí)還可以響應(yīng)客戶端命令; - 執(zhí)行
flushall
命令,清空數(shù)據(jù)庫所有數(shù)據(jù),意義不大; - 執(zhí)行
shutdown
命令,保證服務(wù)器正常關(guān)閉且不丟失任何數(shù)據(jù),意義也不大。
通過RDB文件恢復(fù)數(shù)據(jù)
在實(shí)際開發(fā)中,一般會(huì)考慮到物理機(jī)硬盤損壞的情況,所以我們會(huì)選擇備份 dump.rdb
文件。將備份的 dump.rdb
文件拷貝到 redis
的安裝目錄的 bin
目錄下,重啟 redis
服務(wù)即可。
優(yōu)點(diǎn)
RDB
是一個(gè)非常緊湊的文件,非常適用于數(shù)據(jù)集的備份;RDB
是一個(gè)緊湊的單一文件,很方便傳送到另一個(gè)遠(yuǎn)端數(shù)據(jù)中心或者亞馬遜的S3(可能加密),非常適用于災(zāi)難恢復(fù);Redis
的主進(jìn)程不進(jìn)行I/O
操作,確保了極高的性能;- 適合大規(guī)模數(shù)據(jù)的恢復(fù),對(duì)于數(shù)據(jù)的完整性和一致性要求不高的話,
RDB
比AOF
方式更加高效。
缺點(diǎn)
- 在
Redis
意外宕機(jī)時(shí),你可能會(huì)丟失幾分鐘的數(shù)據(jù);
- RDB
- fork
- fork
- Redis
- CPU
- AOF
- fork
AOF
為了解決 RDB
方式在宕機(jī)時(shí)丟失數(shù)據(jù)過多的問題,從 1.1
版本開始, Redis
增加了一種 durable
的持久化方式: AOF
。
AOF
是 Append Only File
的縮寫,默認(rèn)不開啟。 AOF
以日志的形式來記錄每個(gè)寫操作,只允許追加文件但不可以改寫文件,當(dāng)服務(wù)器重啟的時(shí)候會(huì)重新執(zhí)行這些命令來恢復(fù)原始的數(shù)據(jù)。
我們?cè)賮砜匆幌屡渲梦募械?nbsp;APPEND ONLY MODE
:
配置文件
appendonly no
默認(rèn)為關(guān)閉狀態(tài),改為 yes
打開持久化。 AOF
和 RDB
可以同時(shí)啟用而不會(huì)出現(xiàn)問題。
appendfilename "appendonly.aof"
文件默認(rèn)名稱,啟動(dòng)即創(chuàng)建。加載 先于 dump.rdb
文件
appendfsync
同步策略:系統(tǒng)函數(shù) fsync()
告訴操作系統(tǒng)在磁盤上實(shí)際寫入數(shù)據(jù)。 Redis
支持三種不同的模式
- appendfsync always //每次發(fā)生數(shù)據(jù)變更會(huì)被立即記錄到磁盤,性能較差但數(shù)據(jù)完整性比較好
- appendfsync everysec //默認(rèn)推薦,異步操作,每秒記錄,如果宕機(jī),有1秒內(nèi)數(shù)據(jù)丟失
- appendfsync no //不同步,只有在操作系統(tǒng)需要時(shí)在刷新數(shù)據(jù)
要想了解接下來的配置內(nèi)容,先得說一下“日志重寫”的原理:
重寫
由于 AOF
采用的是將命令追加到文件末尾的方式,所以隨著寫入命令的不斷增加, AOF
文件的體積會(huì)變得越來越大。為避免出現(xiàn)此種情況,新增了重寫機(jī)制:可以在不打斷服務(wù)客戶端的情況下,對(duì) AOF
文件進(jìn)行重建( rebuild
)。
重寫觸發(fā):通過執(zhí)行 bgrewriteaof
命令,可以生成一個(gè)新的 AOF
文件,該文件包含重建當(dāng)前數(shù)據(jù)集所需的 最少 命令。 Redis 2.2
需手動(dòng)執(zhí)行該命令, Redis 2.4
則可以通過修改配置文件的方式自動(dòng)觸發(fā)(配置在下邊涉及)。
重寫原理:
Redis
執(zhí)行系統(tǒng)函數(shù)fork()
,創(chuàng)建一個(gè)子進(jìn)程(與主進(jìn)程完全一致);- 子進(jìn)程開始將新
AOF
文件的內(nèi)容寫入到臨時(shí)文件; - 對(duì)于所有新執(zhí)行的寫入命令,父進(jìn)程一邊將它們累積到一個(gè)內(nèi)存緩存中,一邊將這些改動(dòng)追加到現(xiàn)有
AOF
文件的末尾,這樣即使在重寫的中途發(fā)生停機(jī),現(xiàn)有的AOF
文件也是安全的; - 當(dāng)子進(jìn)程完成重寫工作時(shí),它給父進(jìn)程發(fā)送一個(gè)信號(hào),父進(jìn)程在接收到信號(hào)之后,將內(nèi)存緩存中的所有數(shù)據(jù)追加到新
AOF
文件的末尾。 Redis
原子地用新文件替換舊文件,之后所有命令都會(huì)直接追加到新AOF
文件的末尾。
no-appendfsync-on-rewrite no
當(dāng)我們同時(shí)執(zhí)行主進(jìn)程的 寫操作 和子進(jìn)程的 重寫 操作時(shí),兩者都會(huì)操作磁盤,而重寫往往會(huì)涉及到大量的磁盤操作,這樣就會(huì)造成主進(jìn)程在寫 aof
文件的時(shí)候出現(xiàn)阻塞的情形。
為了解決這個(gè)問題, no-appendfsync-on-rewrite
參數(shù)出場(chǎng)了。
- 如果該參數(shù)設(shè)置為
no
,是最安全的方式,不會(huì)丟失數(shù)據(jù),但是要忍受阻塞的問題;
- yes
- appendfsync
- no
- redis
- linux
因此,如果應(yīng)用系統(tǒng)無法忍受延遲,而可以容忍少量的數(shù)據(jù)丟失,則設(shè)置為 yes
;如果應(yīng)用系統(tǒng)無法忍受數(shù)據(jù)丟失,則設(shè)置為 no
。
auto-aof-rewrite-percentage 100
重寫百分比,默認(rèn)為上次重寫后 aof
文件大小的一倍。
auto-aof-rewrite-min-size 64mb
重寫觸發(fā)的最小值:64mb。
根據(jù) auto-aof-rewrite-min-size
和 auto-aof-rewrite-percentage
參數(shù)確定自動(dòng)觸發(fā)時(shí)機(jī)。 Redis
會(huì)記錄上次重寫時(shí)的 AOF
大小,默認(rèn)配置是當(dāng) AOF
文件大小是上次 rewrite
后大小的一倍且文件大于 64M
時(shí)觸發(fā)。
大型互聯(lián)網(wǎng)公司一般都是 3G
起步
aof-load-truncated yes
當(dāng) AOF
文件被截?cái)鄷r(shí),即 AOF
文件的最后命令不完整,如果此時(shí)啟動(dòng) Redis
,會(huì)將 AOF
數(shù)據(jù)加載回內(nèi)存,此時(shí)便會(huì)出現(xiàn)問題。
- yes:加載一個(gè)截?cái)嗟?nbsp;
AOF
,Redis
服務(wù)器開始發(fā)出日志,通知用戶該事件; -
no:服務(wù)器將中止并出現(xiàn)錯(cuò)誤,拒絕啟動(dòng)。
當(dāng)我們得知 AOF
文件報(bào)錯(cuò)時(shí),可以用以下方法來修復(fù)出錯(cuò)的 AOF
文件:
-
為現(xiàn)有的
AOF
文件創(chuàng)建一個(gè)備份; -
使用
Redis
附帶的redis-check-aof
命令,對(duì)原來的AOF
文件進(jìn)行修復(fù);-
redis-check-aof –fix
-
redis-check-aof --fix appendonly.aof
修復(fù)命令,殺光不符合規(guī)范的語法
-
-
( 可選 )使用
diff -u
對(duì)比修復(fù)后的AOF
文件和原始AOF
文件的備份,查看兩個(gè)文件之間的不同之處; -
重啟
Redis
服務(wù)器,等待服務(wù)器載入修復(fù)后的AOF
文件,并進(jìn)行數(shù)據(jù)恢復(fù)。
aof-use-rdb-preamble yes
在重寫 AOF
文件時(shí), Redis
能夠在 AOF
文件中使用 RDB
前導(dǎo),以加快重寫和恢復(fù)速度。啟用此選項(xiàng)后,重寫的 AOF
文件由兩個(gè)不同的節(jié)組成: RDB file
、 AOF tail
加載 Redis
時(shí),會(huì)識(shí)別 AOF
文件以 Redis 字符串開頭,并加載帶前綴的 RDB
文件,然后繼續(xù)加載 AOF
尾部。
理論
優(yōu)點(diǎn)
- 數(shù)據(jù)的完整性和一致性更高,
AOF
的持久化通過使用不同的策略,最多丟失1秒的數(shù)據(jù); AOF
文件是一個(gè)只進(jìn)行追加的日志文件,不需要寫入seek
;
- Redis
- AOF
- AOF
AOF
文件記錄的寫入操作以 Redis
協(xié)議的格式保存,容易讀懂,容易對(duì)文件進(jìn)行分析;缺點(diǎn)
- 對(duì)于相同的數(shù)據(jù)集來說,
AOF
文件的體積通常要大于RDB
文件的體積;
- fsync
- AOF
- RDB
在一般情況下,每秒 fsync
的性能依然非常高,而關(guān)閉 fsync
可以讓 AOF
的速度和 RDB
一樣快, 即使在高負(fù)荷之下也是如此。不過在處理巨大的寫入載入時(shí), RDB
可以提供更有保證的最大延遲時(shí)間( latency
)。
對(duì)比與總結(jié)
如何選擇使用哪種持久化方式?
一般來說,如果想達(dá)到足以媲美 PostgreSQL
的數(shù)據(jù)安全性,應(yīng)該同時(shí)使用兩種持久化功能。
如果非常關(guān)心數(shù)據(jù),但仍然可以承受數(shù)分鐘以內(nèi)的數(shù)據(jù)丟失,那么可以只使用 RDB
持久化。
由于AOF持久化的實(shí)時(shí)性更好,即當(dāng)進(jìn)程意外退出時(shí)丟失的數(shù)據(jù)更少,因此 AOF
是目前 主流 的持久化方式。
有很多用戶都只使用 AOF
持久化,但我們并不推薦這種方式:因?yàn)槎〞r(shí)生成 RDB
快照( snapshot
)非常便于進(jìn)行數(shù)據(jù)庫備份,并且 RDB
恢復(fù)數(shù)據(jù)集的速度也要比 AOF
恢復(fù)的速度要快。
AOF和RDB之間的相互作用
在版本號(hào)大于等于 2.4
的 Redis
中, BGSAVE
執(zhí)行的過程中,不可以執(zhí)行 BGREWRITEAOF
。反過來說,在 BGREWRITEAOF
執(zhí)行的過程中,也不可以執(zhí)行 BGSAVE
。這可以防止兩個(gè) Redis
后臺(tái)進(jìn)程同時(shí)對(duì)磁盤進(jìn)行大量的 I/O
操作。
如果 BGSAVE
正在執(zhí)行,并且用戶顯示地調(diào)用 BGREWRITEAOF
命令,那么服務(wù)器將向用戶回復(fù)一個(gè) OK
狀態(tài), 并告知用戶 BGREWRITEAOF
已經(jīng)被預(yù)定執(zhí)行:一旦 BGSAVE
執(zhí)行完畢, BGREWRITEAOF
就會(huì)正式開始。
當(dāng) Redis
啟動(dòng)時(shí),如果 RDB
持久化和 AOF
持久化都被打開了, 那么程序會(huì)優(yōu)先使用 AOF
文件來恢復(fù)數(shù)據(jù)集,因?yàn)?nbsp;AOF
文件所保存的數(shù)據(jù)通常是最完整的。
備份redis數(shù)據(jù)
- cron job
- RDB
- RDB
find
命令來刪除過期的快照;RDB
備份到你的數(shù)據(jù)中心之外,或者至少是備份到你運(yùn)行 Redis
服務(wù)器的物理機(jī)器之外。性能建議
在實(shí)際應(yīng)用時(shí),因?yàn)?nbsp;RDB
文件只用作后備用途,建議只在 slave
上持久化 RDB
文件,而且只需要15分鐘備份一次就夠了,只保留 save 900 1
這條規(guī)則。
如果開啟 AOF
,好處是在最惡劣情況下也只會(huì)丟失不超過2秒數(shù)據(jù),啟動(dòng)腳本較簡(jiǎn)單只 load
自己的 AOF
文件就可以了。代價(jià)一是帶來了持續(xù)的 IO
,二是 AOF rewrite
的最后將 rewrite
過程中產(chǎn)生的新數(shù)據(jù)寫到新文件造成的阻塞幾乎是不可避免的。
只要硬盤許可,應(yīng)該盡量減少 AOF rewrite
的頻率, AOF
重寫的基礎(chǔ)大小默認(rèn)值 64M
太小了,可以設(shè)置到 5G
以上。默認(rèn)超過原大小的100%時(shí)重寫可以改到適當(dāng)?shù)臄?shù)值。
如果不開啟 AOF
,僅靠 Master-Slave Replication
實(shí)現(xiàn)高可用性也可以。能省掉一大筆 IO
,也減少了 rewrite
時(shí)帶來的系統(tǒng)波動(dòng)。代價(jià)是如果 Master/Slave
同時(shí)倒掉,會(huì)丟失十幾分鐘的數(shù)據(jù),啟動(dòng)腳本也要比較兩個(gè) Master/Slave
中的 RDB
文件,載入較新的那個(gè)。