10.1 Redis持久化操作概述
Redis是一個(gè)功能強(qiáng)大、讀/寫速度極快、性能優(yōu)越的數(shù)據(jù)庫。它的性能之所以強(qiáng)大在很大程度上是因?yàn)樗鼘⑺袛?shù)據(jù)存儲(chǔ)在內(nèi)存中,使得讀/寫速度及相關(guān)性能得到很大的提升。但當(dāng)Redis進(jìn)程退出或者重啟后,所有存儲(chǔ)在內(nèi)存中的數(shù)據(jù)就會(huì)丟失。
在實(shí)際應(yīng)用中希望Redis在重啟后可以保證數(shù)據(jù)不丟失,比如:
●利用Redis作為數(shù)據(jù)庫存儲(chǔ)數(shù)據(jù),長(zhǎng)久保存數(shù)據(jù)
●利用Redis作為緩存服務(wù)器緩存大量數(shù)據(jù)。但當(dāng)緩存被穿透后,會(huì)對(duì)其性能造成較大影響。更嚴(yán)重的是當(dāng)所有緩存同時(shí)失效時(shí)會(huì)導(dǎo)致緩存雪崩,從而使得服務(wù)器停止服務(wù)。
為了解決Redis服務(wù)器重啟后數(shù)據(jù)丟失的問題,Redis采用某種方式將數(shù)據(jù)從內(nèi)存保存到硬盤中,使得服務(wù)器重啟之后Redis可以根據(jù)硬盤中保存的數(shù)據(jù)進(jìn)行恢復(fù),這個(gè)過程就是持久化,過程產(chǎn)生的文件就是持久化文件。利用Redis的持久化文件就能實(shí)現(xiàn)數(shù)據(jù)恢復(fù),從而達(dá)到保存數(shù)據(jù)不丟失的目的。
Redis支持兩種持久化方式:AOF持久化和RDB持久化。AOF持久化方式會(huì)將每次執(zhí)行的命令及時(shí)保存到硬盤中;RDB持久化方式會(huì)根據(jù)指定的規(guī)則“定時(shí)”將內(nèi)存中的數(shù)據(jù)保存到硬盤中。AOF 持久化方式的實(shí)時(shí)性更好,也就是當(dāng)進(jìn)程意外退出時(shí),丟失的數(shù)據(jù)更少。在通常情況下,這兩種持久化方式可以單獨(dú)使用,但在更多情況下,可以將二者結(jié)合起來使用。
10.2 Redis持久化機(jī)制AOF
AOF(Append Only File)持久化保存服務(wù)器執(zhí)行的所有寫操作命令到單獨(dú)的日志文件中,在服務(wù)器重啟時(shí)通過加載日志文件中的命令并執(zhí)行來恢復(fù)數(shù)據(jù)。這個(gè)日志文件就是 AOF文件,Redis以Redis協(xié)議格式來保存AOF文件中的所有命令,新命令會(huì)被追加到文件的結(jié)尾。在服務(wù)器的后臺(tái),AOF文件還會(huì)被重寫(Rewrite),使AOF文件的體積不會(huì)大于保存數(shù)據(jù)集狀態(tài)所需的實(shí)際大小。
當(dāng)使用Redis來存儲(chǔ)一些需要長(zhǎng)久保存的數(shù)據(jù)時(shí),一般需要打開AOF持久化來降低進(jìn)程突然中止導(dǎo)致數(shù)據(jù)丟失的風(fēng)險(xiǎn)。
10.2.1 AOF持久化的配置
默認(rèn)情況下AOF持久化沒有開啟,如果要采用AOF持久化方式保存數(shù)據(jù)要開啟AOF 持久化??梢酝ㄟ^修改配置文件redis.conf中的appendonly參數(shù)開啟:
appendonly yes
開啟AOF持久化后,服務(wù)器每執(zhí)行一條寫命令,Redis就會(huì)把該命令寫入硬盤的AOF 文件中。AOF文件位置可以通過dir參數(shù)來設(shè)置。AOF文件的默認(rèn)名稱是appendonly.aof,可以通過appendfilename參數(shù)來修改AOF文件的名稱:
appendfilename "appendonly.aof"
與AOF持久化相關(guān)的配置總結(jié)如下:
●appendonly no:是否開啟AOF持久化,默認(rèn)為no(不開啟),設(shè)置為yes表示開啟
AOF持久化
●appendfilename "appendonly.aof":AOF文件名,可以修改它
●dir ./:AOF文件和RDB文件所在目錄
●appendfsync everysec:fsync持久化策略
●no-appendfsync-on-rewrite no:在重寫AOF文件的過程中是否禁止fsync。如果這個(gè)參數(shù)值設(shè)置為yes(開啟)則可以減輕重寫AOF文件時(shí)CPU和硬盤的負(fù)載,但同時(shí)可能會(huì)丟失重寫AOF文件過程中的數(shù)據(jù),需要在負(fù)載與安全性之間進(jìn)行平衡
●auto-aof-rewrite-percentage 100:指定Redis重寫AOF文件的條件,默認(rèn)為100,它會(huì)對(duì)比上次生成的AOF文件大小。如果當(dāng)前AOF文件的增長(zhǎng)量大于上次AOF文件的 100%就會(huì)觸發(fā)重寫操作,如果將該選項(xiàng)設(shè)置為0則不會(huì)觸發(fā)重寫操作
●auto-aof-rewrite-min-size 64mb:指定觸發(fā)重寫操作的AOF文件的大小,默認(rèn)為64MB。如果當(dāng)前AOF文件的大小低于該值,此時(shí)就算當(dāng)前文件的增量比例達(dá)到了auto-aof-rewrite-percentage選項(xiàng)所設(shè)置的條件,也不會(huì)觸發(fā)重寫操作。換句話說,只有同時(shí)滿足以上這兩個(gè)選項(xiàng)所設(shè)置的條件才會(huì)觸發(fā)重寫操作
●auto-load-truncated yes:當(dāng)AOF文件結(jié)尾遭到損壞,Redis在啟動(dòng)時(shí)是否仍加載
AOF文件
10.1.2 AOF持久化的實(shí)現(xiàn)
在開啟AOF持久化之后,Redis服務(wù)器每執(zhí)行一條寫命令,AOF文件都會(huì)記錄這條寫命令。因?yàn)樾枰獙?shí)時(shí)記錄Redis的每條寫命令,因此AOF不需要觸發(fā)就能實(shí)現(xiàn)持久化。
AOF持久化的實(shí)現(xiàn)過程如下:
(1)命令追加(append):Redis服務(wù)器每執(zhí)行一條寫命令,這條寫命令都會(huì)被追加到緩存區(qū)aof_buf中。
在追加命令的過程中,Redis并沒有直接將命令寫入文件中,而是將命令追加到緩存區(qū)aof_buf的末尾。這樣做的目的是避免每次執(zhí)行的命令都直接寫入硬盤中,會(huì)導(dǎo)致硬盤 I/O的負(fù)載過大,使性能下降。命令追加的格式使用Redis命令請(qǐng)求的協(xié)議格式,它是一種純文本格式,具有很多優(yōu)點(diǎn),如兼容性好、易處理、易讀取、操作簡(jiǎn)單、可避免二次開銷等。比如,執(zhí)行以下命令:
127.0.0.1:6379>SET name redis
OK
服務(wù)器在接收到客戶端發(fā)送過來的SET命令后,會(huì)將下面的協(xié)議格式內(nèi)容追加到緩存區(qū)aof_buf的末尾:
*3\r\n3\r\nname\r\n$5\r\nredis\r\n
在AOF文件中,除了用于切換數(shù)據(jù)庫的select命令是由Redis添加的,其他寫命令都是客戶端發(fā)送過來的。
(2)AOF持久化文件寫入(write)和文件同步(sync):根據(jù)appendfsync參數(shù)設(shè)置的不
同的同步策略,將緩存區(qū)aof_buf中的數(shù)據(jù)內(nèi)容同步到硬盤中。
Redis為AOF緩存區(qū)的同步提供了多種策略,策略涉及操作系統(tǒng)的write和fsync函數(shù)。為提高文件的寫入效率,當(dāng)用戶調(diào)用write函數(shù)將數(shù)據(jù)寫入文件中時(shí),操作系統(tǒng)會(huì)將這些數(shù)據(jù)暫存到一個(gè)內(nèi)存緩存區(qū)中,當(dāng)這個(gè)緩存區(qū)被填滿或者超過了指定時(shí)限后才會(huì)將緩存區(qū)中的數(shù)據(jù)寫入硬盤中,這樣做既提高了效率又保證了安全性。
Redis的服務(wù)器進(jìn)程是一個(gè)事件循環(huán)(loop),這個(gè)事件循環(huán)中的文件事件負(fù)責(zé)接收客戶端的命令請(qǐng)求,處理之后向客戶端發(fā)送命令回復(fù),其中的時(shí)間事件則負(fù)責(zé)執(zhí)行像 serverCron函數(shù)這樣需要定時(shí)運(yùn)行的函數(shù)。
服務(wù)器在處理文件事件時(shí),可能會(huì)執(zhí)行客戶端發(fā)送過來的寫命令,使得一些命令被追加到緩存區(qū)aof_buf中。因此在服務(wù)器每次結(jié)束一個(gè)事件循環(huán)之前都會(huì)調(diào)用flushAppendOnlyFile函數(shù)來決定是否將緩存區(qū)aof_buf中的數(shù)據(jù)寫入和保存到AOF文件中。
flushAppendOnlyFile函數(shù)的運(yùn)行與服務(wù)器配置的appendfsync參數(shù)有關(guān)。appendfsync 參數(shù)具有多個(gè)值,具體如下:
●當(dāng)appendfsync參數(shù)的值為always時(shí),flushAppendOnlyFile函數(shù)會(huì)將緩存區(qū)aof_buf 中的所有內(nèi)容寫入并同步到AOF文件中。服務(wù)器的文件事件每循環(huán)一次,都要將緩存區(qū)aof_buf中的所有內(nèi)容寫入AOF文件中并同步AOF文件,這個(gè)過程在無形中加大了硬盤I/O的負(fù)載,使得硬盤I/O成為性能瓶頸,從而嚴(yán)重降低Redis的性能。所以使用always的效率比較低,但從安全性考慮,使用always是安全的。即使Redis 服務(wù)器出現(xiàn)故障,AOF持久化也只會(huì)丟失近一次事件循環(huán)中的命令數(shù)據(jù)。
●當(dāng)appendfsync參數(shù)的值為no時(shí),flushAppendOnlyFile函數(shù)會(huì)將緩存區(qū)aof_buf中的所有內(nèi)容寫入AOF文件中,但不會(huì)同步AOF文件,至于什么時(shí)候同步則交給操作系統(tǒng)來決定,通常同步周期為30秒。在使用no時(shí),AOF文件的同步不可控且緩存區(qū)中的內(nèi)容會(huì)越來越多,一旦發(fā)生故障將會(huì)丟失大量數(shù)據(jù)。因?yàn)椴挥脠?zhí)行AOF同步操作,所以AOF寫入數(shù)據(jù)的速度總是快的,效率也很高。
●當(dāng)appendfsync參數(shù)的值為everysec時(shí),flushAppendOnlyFile函數(shù)會(huì)將緩存區(qū)aof_buf 中的所有內(nèi)容寫入AOF文件中。而AOF文件的同步操作則由一個(gè)專門的文件同步線程負(fù)責(zé),每秒執(zhí)行一次。如果上次同步AOF文件的時(shí)間距離現(xiàn)在超過了1秒,同步線程就會(huì)再次對(duì)AOF文件進(jìn)行同步。在使用everysec時(shí),AOF文件的寫入與同步效率非常高,它是前面兩種策略的折中,是性能和數(shù)據(jù)安全性的平衡,既滿足了效率要求又考慮了安全性,推薦使用。
10.1.3 AOF文件重寫
1.AOF文件重寫的目的
定期重寫AOF文件以達(dá)到壓縮的目的。AOF持久化的實(shí)現(xiàn)是通過保存被執(zhí)行的寫命令來保存數(shù)據(jù)庫數(shù)據(jù)的。隨著服務(wù)器運(yùn)行時(shí)間的增加,AOF文件的內(nèi)容數(shù)據(jù)會(huì)越來越大,文件所占據(jù)的內(nèi)存也會(huì)變大。過大的AOF文件會(huì)影響服務(wù)器的正常運(yùn)行,在執(zhí)行數(shù)據(jù)恢復(fù)時(shí)將會(huì)耗費(fèi)更多的時(shí)間。
為解決AOF文件體積過大的問題,Redis提供了AOF文件重寫的功能,就是定期重寫 AOF文件以減小AOF文件的體積。其實(shí)AOF文件重寫就是把Redis進(jìn)程內(nèi)的數(shù)據(jù)轉(zhuǎn)化為寫命令然后同步到新的AOF文件中。在重寫的過程中Redis服務(wù)器會(huì)創(chuàng)建一個(gè)新的AOF文件來替代現(xiàn)有的AOF文件,新舊兩個(gè)AOF文件所保存的數(shù)據(jù)庫狀態(tài)相同,但新的AOF文件不會(huì)包含冗余命令。
Redis將生成新的AOF文件替換舊的AOF文件的功能命名為AOF文件重寫。實(shí)際上,AOF文件重寫并不會(huì)對(duì)舊的AOF文件進(jìn)行讀取、寫入操作,這個(gè)功能是通過讀取服務(wù)器當(dāng)前的數(shù)據(jù)庫狀態(tài)來實(shí)現(xiàn)的。
通過客戶端向服務(wù)器端發(fā)送多條 RPUSH 命令,向列表中添加多個(gè)顏色元素,并成功執(zhí)行。操作如下:
127.0.0.1:6379> RPUSH color "red" "blue" #向列表color中添加多個(gè)顏色元素 (integer) 2
127.0.0.1:6379> RPUSH color "yellow" "green" "black" (integer) 5
127.0.0.1:6379> LPOP color #移除并返回列表color的頭元素 "red"
127.0.0.1:6379> LPOP color
"blue"
127.0.0.1:6379> RPUSH color "pink" "white" (integer) 5
Redis服務(wù)器在開啟了AOF持久化之后,就會(huì)保持當(dāng)前列表color鍵的狀態(tài),在AOF文件中寫入5條命令。如果服務(wù)器想用少的命令來保存列表color鍵的狀態(tài),就要利用AOF 文件重寫功能。簡(jiǎn)單的方法不是去讀取和分析現(xiàn)有AOF文件的內(nèi)容,而是直接從數(shù)據(jù)庫中讀取出列表color鍵的值,用RPUSH color "yellow" "green" "black" "pink" "white"命令來代替保存在AOF中的5條命令,這樣就實(shí)現(xiàn)了AOF文件重寫功能。
除上面所說的列表鍵之外,其他類型的鍵也可以用同樣的方法去減少AOF文件中的命令數(shù)量,也就是直接去數(shù)據(jù)庫中讀取該鍵所存儲(chǔ)的值,然后用一條命令記錄來代替之前這個(gè)鍵值對(duì)的多條寫命令,這就是AOF文件重寫功能的原理。
為什么AOF文件重寫可以壓縮AOF文件?原因有如下幾點(diǎn):
● AOF文件重寫功能會(huì)丟棄過期的數(shù)據(jù),也就是過期的數(shù)據(jù)不會(huì)被寫入AOF文件中
● AOF文件重寫功能會(huì)丟棄無效的命令,無效的命令將不會(huì)被寫入AOF文件中。無效命令包括重復(fù)設(shè)置某個(gè)鍵值對(duì)時(shí)的命令、刪除某些數(shù)據(jù)時(shí)的命令等
● AOF文件重寫功能可以將多條命令合并為一條命令,然后寫入AOF文件中
在實(shí)際應(yīng)用中,Redis為了防止在執(zhí)行命令時(shí)造成客戶端緩存區(qū)溢出,重寫程序在處理列表、哈希表、集合及有序集合這4種可能會(huì)帶有多個(gè)元素的鍵時(shí),會(huì)提前檢查這些鍵所包含的元素個(gè)數(shù)。假如鍵所包含的元素個(gè)數(shù)大于redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD常量的值,那么重寫程序會(huì)使用多條命令來記錄這個(gè)鍵的值。REDIS_AOF_REWRITE_ITEMS_PER_CMD 常量的值為 64。
2.AOF文件重寫的觸發(fā)方式
AOF文件重寫的觸發(fā)有兩種方式:手動(dòng)觸發(fā)和自動(dòng)觸發(fā)。
l .手動(dòng)觸發(fā):執(zhí)行BGREWRITEAOF命令觸發(fā)AOF文件重寫。該命令與BGSAVE命令相似,都是啟動(dòng)(fork)子進(jìn)程完成具體的工作且都在啟動(dòng)時(shí)阻塞。如圖所示為執(zhí)行BGREWRITEAOF命令觸發(fā)AOF文件重寫。

?自動(dòng)觸發(fā):自動(dòng)觸發(fā)AOF文件重寫是通過設(shè)置Redis配置文件中auto-aof-rewrite-
percentage和auto-aof-rewrite-min-size參數(shù)的值,以及aof_current_size和 aof_base_size狀態(tài)來確定何時(shí)觸發(fā)的。
auto-aof-rewrite-percentage參數(shù)是在執(zhí)行AOF文件重寫時(shí),當(dāng)前AOF文件的大小
(aof_current_size)和上一次AOF文件重寫時(shí)的大?。╝of_base_size)的比值,默認(rèn)為 100。 auto-aof-rewrite-min-size參數(shù)設(shè)置了執(zhí)行AOF文件重寫時(shí)的最小體積,默認(rèn)為 64MB。
使用 CONFIG GET 命令來查看上述參數(shù)的值,操作如下:
127.0.0.1:6379> CONFIG GET auto-aof-rewrite-percentage
1)"auto-aof-rewrite-percentage"
2)"100"
127.0.0.1:6379> CONFIG GET auto-aof-rewrite-min-size
1)"auto-aof-rewrite-min-size"
2)"67108864"
使用INFO PERSISTENCE命令來查看AOF持久化的相關(guān)狀態(tài),操作如下:
127.0.0.1:6379> INFO PERSISTENCE
Persistence
… aof_enabled:0 aof_rewrite_in_progress:0 aof_rewrite_scheduled:0 aof_last_rewrite_time_sec:0 aof_current_rewrite_time_sec:-1 aof_last_bgrewrite_status:ok aof_last_write_status:ok aof_last_cow_size:184320
只有當(dāng)Redis服務(wù)器同時(shí)滿足auto-aof-rewrite-percentage和auto-aof-rewrite-min-size參數(shù)值時(shí)才會(huì)觸發(fā)AOF文件重寫。
3.AOF文件后臺(tái)重寫
在實(shí)現(xiàn)AOF文件重寫的過程中,會(huì)調(diào)用aof_rewrite函數(shù)創(chuàng)建一個(gè)新的AOF文件,同時(shí)將舊的AOF文件的命令重寫到新的AOF文件中,在這個(gè)過程中會(huì)執(zhí)行大量的寫入操作會(huì)使得這個(gè)函數(shù)的線程被長(zhǎng)時(shí)間阻塞。Redis服務(wù)器使用單線程來處理命令請(qǐng)求。如果讓服務(wù)器直接調(diào)用aof_rewrite重寫函數(shù),那么在AOF文件重寫期間服務(wù)器將不能繼續(xù)執(zhí)行其他命令會(huì)一直處于阻塞狀態(tài)。因此,Redis將AOF文件重寫程序放到了一個(gè)子進(jìn)程中執(zhí)行,這樣做的好處是:
●子進(jìn)程在執(zhí)行AOF文件重寫的過程中,Redis服務(wù)器進(jìn)程可以繼續(xù)處理新的命令請(qǐng)求。
●子進(jìn)程帶有服務(wù)器進(jìn)程的數(shù)據(jù)副本,使用子進(jìn)程可以在使用鎖的情況下,保證數(shù)據(jù)的安全性。
使用子進(jìn)程會(huì)導(dǎo)致數(shù)據(jù)庫狀態(tài)不一致,原因是:當(dāng)子進(jìn)程進(jìn)行AOF文件重寫的時(shí)候, Redis服務(wù)器可以繼續(xù)執(zhí)行來自客戶端的命令請(qǐng)求,就會(huì)有新的命令對(duì)現(xiàn)有數(shù)據(jù)庫狀態(tài)進(jìn)行修改,進(jìn)而使得服務(wù)器當(dāng)前的數(shù)據(jù)庫狀態(tài)與重寫的AOF文件所保存的數(shù)據(jù)庫狀態(tài)不一致。
為解決使用子進(jìn)程導(dǎo)致數(shù)據(jù)庫狀態(tài)不一致的問題,Redis服務(wù)器設(shè)置了一個(gè)AOF文件重寫緩存區(qū)。這個(gè)AOF文件重寫緩存區(qū)在服務(wù)器創(chuàng)建子進(jìn)程之后開始使用,可以利用它來解決數(shù)據(jù)庫狀態(tài)不一致的問題。當(dāng)Redis服務(wù)器成功執(zhí)行完一條寫命令后,它會(huì)同時(shí)將這條寫命令發(fā)送給AOF文件緩存區(qū)(aof_buf)和AOF文件重寫緩存區(qū)。子進(jìn)程在執(zhí)行AOF 文件重寫的過程中,服務(wù)器進(jìn)程的執(zhí)行過程如下:
(1)服務(wù)器接收到來自客戶端的命令請(qǐng)求,并成功執(zhí)行。
(2)服務(wù)器將執(zhí)行后的寫命令轉(zhuǎn)化為對(duì)應(yīng)的協(xié)議格式,然后追加到 AOF 文件緩存區(qū)
(aof_buf)中。
(3)服務(wù)器再將執(zhí)行后的寫命令追加到 AOF 文件重寫緩存區(qū)中。
以上過程用流程圖表示如圖所示:

有了AOF文件重寫緩存區(qū),就可以保證數(shù)據(jù)庫狀態(tài)的一致性。AOF文件緩存區(qū)的內(nèi)容會(huì)被定期寫入和同步到AOF文件中,AOF文件的寫入和同步不會(huì)因?yàn)锳OF文件重寫緩存區(qū)的引入而受到影響。當(dāng)服務(wù)器創(chuàng)建子進(jìn)程之后,服務(wù)器執(zhí)行的所有寫命令都會(huì)同時(shí)被追加到AOF文件緩存區(qū)和AOF文件重寫緩存區(qū)中。
如果子進(jìn)程完成了AOF文件重寫的工作,它就會(huì)發(fā)送一個(gè)完成信號(hào)給父進(jìn)程。當(dāng)父進(jìn)程接收到這個(gè)信號(hào)后,就會(huì)調(diào)用信號(hào)處理函數(shù)繼續(xù)執(zhí)行以下工作:
(1)將AOF文件重寫緩存區(qū)中的所有內(nèi)容寫入新的AOF文件中。新的AOF文件所保存的數(shù)據(jù)庫狀態(tài)與服務(wù)器當(dāng)前的數(shù)據(jù)庫狀態(tài)保持一致
(2)修改新的AOF文件的文件名,新生成的AOF文件將會(huì)覆蓋現(xiàn)有(舊)的AOF 文件,完成新舊兩個(gè)文件的互換
在完成上述兩個(gè)步驟之后,就完成了一次AOF文件后臺(tái)重寫工作。 在整個(gè)AOF文件
后臺(tái)重寫的過程中,只有在信號(hào)處理函數(shù)執(zhí)行的過程中,服務(wù)器進(jìn)程才會(huì)被阻塞,在其他時(shí)候不存在阻塞情況。
10.1.4 AOF文件處理
如果開啟了AOF持久化,那么在Redis服務(wù)器啟動(dòng)的時(shí)候就會(huì)先加載AOF文件中的數(shù)據(jù)來恢復(fù)數(shù)據(jù)庫數(shù)據(jù)。因?yàn)锳OF文件中保存了數(shù)據(jù)庫狀態(tài)所需的所有寫命令,所以服務(wù)器讀取并執(zhí)行AOF文件中的寫命令,就可以還原服務(wù)器關(guān)閉之前的數(shù)據(jù)庫狀態(tài)。這個(gè)過程具體如下:
(1)創(chuàng)建一個(gè)偽客戶端,用于執(zhí)行AOF文件中的寫命令。這個(gè)偽客戶端是一個(gè)不帶網(wǎng)絡(luò)連接的客戶端。因?yàn)橹荒茉诳蛻舳说纳舷挛闹胁拍軋?zhí)行Redis的命令,而在AOF 文件中包含了Redis服務(wù)器啟動(dòng)加載AOF文件時(shí)所使用的所有命令而不是網(wǎng)絡(luò)連接,所以服務(wù)器創(chuàng)建了一個(gè)不帶網(wǎng)絡(luò)連接的偽客戶端來執(zhí)行AOF文件中的寫命令
(2)讀取AOF文件中的數(shù)據(jù),分析并提取出AOF文件所保存的一條寫命令
(3)使用偽客戶端執(zhí)行被讀取出的寫命令
(4)重復(fù)執(zhí)行步驟(2)和(3)直到將AOF文件中的所有命令讀取完畢并成功執(zhí)行為止。這個(gè)過程完成之后,就可以將服務(wù)器的數(shù)據(jù)庫狀態(tài)還原為關(guān)閉之前的狀態(tài)。
Redis啟動(dòng)加載AOF文件恢復(fù)數(shù)據(jù)的過程如圖所示:

如果在Redis服務(wù)器啟動(dòng)加載AOF文件時(shí)發(fā)現(xiàn)AOF文件被損壞,那么服務(wù)器會(huì)拒絕加載這個(gè)AOF文件,以此來確保數(shù)據(jù)的一致性不被破壞。而AOF文件被損壞的原因可能是程序正在對(duì)AOF文件進(jìn)行寫入與同步時(shí)服務(wù)器出現(xiàn)停機(jī)故障。如果AOF文件被損壞了,則可以通過以下方法來修復(fù):
●及時(shí)備份現(xiàn)有AOF文件
●利用Redis自帶的redis-check-aof程序,對(duì)原來的AOF文件進(jìn)行修復(fù),命令如下:
$ redis-check-aof –fix
●使用diff -u來對(duì)比原始AOF文件和修復(fù)后的AOF文件,找出這兩個(gè)文件的不同之處
●修復(fù)AOF文件之后,重啟Redis服務(wù)器重新加載,進(jìn)行數(shù)據(jù)恢復(fù)
10.1.5 AOF持久化的優(yōu)劣
AOF持久化具有以下優(yōu)點(diǎn):
●使用AOF持久化會(huì)讓Redis持久化更長(zhǎng):通過設(shè)置不同的fsync策略來達(dá)到更長(zhǎng)的
持久化。具體有 3 種策略。
?當(dāng)有新的寫命令追加到AOF文件末尾時(shí),就執(zhí)行一次 fsync。這種方式雖然速度比較慢,但是很安全。
?設(shè)置為每秒執(zhí)行一次fsync。這種方式速度比較快,如果發(fā)生故障,則只會(huì)丟失 1 秒內(nèi)的數(shù)據(jù),即兼顧了效率與安全性,推薦使用。
?從不執(zhí)行fsync,而是直接將數(shù)據(jù)交給操作系統(tǒng)來處理。這種方式雖然速度比較快,但是安全性比較差,不建議使用。
●兼容性比較好:AOF文件是一個(gè)日志文件,它的作用是記錄服務(wù)器執(zhí)行的所有寫命令。當(dāng)文件因?yàn)槟硹l寫命令寫入失敗時(shí),可以使用 redis-check-aof 進(jìn)行修復(fù),然后繼續(xù)使用。
●支持后臺(tái)重寫:當(dāng) AOF 文件的體積過大時(shí),在后臺(tái)可以自動(dòng)地對(duì) AOF 文件進(jìn)行重寫,因此數(shù)據(jù)庫當(dāng)前狀態(tài)的所有命令集合都會(huì)被重寫到 AOF 文件中。重寫完成后,
Redis 就會(huì)切換到新的 AOF 文件,繼續(xù)執(zhí)行寫命令的追加操作。
●AOF 文件易于讀取和加載:AOF 文件保存了對(duì)數(shù)據(jù)庫的所有寫命令,這些寫命令采用 Redis 協(xié)議格式追加到 AOF 文件中,因此非常容易讀取和加載。
AOF 持久化具有以下缺點(diǎn):
●AOF 文件的體積會(huì)隨著時(shí)間的推移逐漸變大,導(dǎo)致在加載時(shí)速度會(huì)比較慢,進(jìn)而影響數(shù)據(jù)庫狀態(tài)的恢復(fù)速度,性能快速下降。
●根據(jù)所使用的 fsync 策略,使用 AOF 文件恢復(fù)數(shù)據(jù)的速度可能會(huì)慢于使用 RDB 文件恢復(fù)數(shù)據(jù)的速度。
●因?yàn)?AOF 文件的個(gè)別命令,可能會(huì)導(dǎo)致在加載時(shí)失敗,從而無法進(jìn)行數(shù)據(jù)恢復(fù)。
10.2 Redis持久化機(jī)制RDB
Redis持久化機(jī)制RDB與持久化機(jī)制AOF類似,都是為了避免Redis服務(wù)器在內(nèi)存中的數(shù)據(jù)因?yàn)榉?wù)器進(jìn)程的退出丟失而建立的一種持久化機(jī)制。RDB持久化生成的RDB文件是一個(gè)經(jīng)過壓縮的二進(jìn)制文件,也可以稱之為快照文件,通過該文件可以還原生成RDB 文件時(shí)的數(shù)據(jù)庫狀態(tài)。因?yàn)镽DB文件保存在硬盤上,所以就算服務(wù)器停止服務(wù),也可以利用RDB文件來還原數(shù)據(jù)庫狀態(tài)。
10.2.1 RDB持久化
在指定的時(shí)間間隔內(nèi),RDB持久化可以生成數(shù)據(jù)集的時(shí)間點(diǎn)快照。在指定的時(shí)間間隔內(nèi)Redis會(huì)自動(dòng)將內(nèi)存中的所有數(shù)據(jù)生成一份副本并存儲(chǔ)在硬盤上,這個(gè)過程就是“快照”。
1.快照處理的發(fā)生條件
當(dāng)出現(xiàn)以下幾種情況時(shí),Redis會(huì)對(duì)數(shù)據(jù)進(jìn)行快照處理:
●根據(jù)Redis配置文件redis.conf中的配置自動(dòng)進(jìn)行快照(自動(dòng)觸發(fā))
在Redis中,用戶可以根據(jù)實(shí)際需要自行定義快照條件,當(dāng)符合快照條件時(shí),服務(wù)器會(huì)自動(dòng)執(zhí)行快照操作??煺諚l件是在Redis配置文件redis.conf中設(shè)置的,用戶可以自定義,格式為:save m n。它由兩個(gè)參數(shù)構(gòu)成:時(shí)間m和被修改的鍵的個(gè)數(shù)n。當(dāng)在時(shí)間m內(nèi)被修改的鍵的個(gè)數(shù)大于n時(shí),就會(huì)觸發(fā)BGSAVE命令,服務(wù)器就會(huì)自動(dòng)執(zhí)行快照操作。
Redis配置文件redis.conf中的默認(rèn)設(shè)置如下:
save 900 1
save 300 10
save 60 10000
以上3個(gè)快照條件都是以save屬性開頭的,它們之間是“或”的關(guān)系,也就是每次只有其中一個(gè)快照條件會(huì)被執(zhí)行。
?save 900 1:表示在900秒內(nèi)有1個(gè)或1個(gè)以上的鍵被修改就會(huì)進(jìn)行快照處理
?save 300 10:表示在300秒內(nèi)有10個(gè)或10個(gè)以上的鍵被修改就會(huì)進(jìn)行快照處理
?save 60 1000:表示在60秒內(nèi)有1000個(gè)或1000個(gè)以上的鍵被修改就會(huì)進(jìn)行快照處理
Redis的save m n命令是通過serverCron函數(shù)、dirty計(jì)數(shù)器及l(fā)astsave時(shí)間戳來實(shí)現(xiàn)的。
serverCron函數(shù):這是Redis服務(wù)器的周期性操作函數(shù),默認(rèn)每隔100毫秒執(zhí)行一次,它主要的作用是維護(hù)服務(wù)器的狀態(tài)。其中一項(xiàng)工作就是判斷save m n配置的條件是否滿足,如果滿足就會(huì)觸發(fā)執(zhí)行BGSAVE命令。
dirty計(jì)數(shù)器:這是Redis服務(wù)器維持的一種狀態(tài),它主要用于記錄上一次執(zhí)行SAVE 或BGSAVE命令后,服務(wù)器進(jìn)行了多少次狀態(tài)修改(執(zhí)行添加、刪除、修改等操作);當(dāng) SAVE或BGSAVE命令執(zhí)行完成后,服務(wù)器會(huì)將dirty重新設(shè)置為0。dirty計(jì)數(shù)器記錄的是服務(wù)器進(jìn)行了多少次狀態(tài)修改,而不是客戶端執(zhí)行了多少次修改數(shù)據(jù)的命令。
lastsave時(shí)間戳:主要用于記錄服務(wù)器上一次成功執(zhí)行SAVE或BGSAVE命令的時(shí)間,它是Redis服務(wù)器維持的一種狀態(tài)。
dirty計(jì)數(shù)器和lastsave時(shí)間戳屬性都保存在服務(wù)器狀態(tài)的redisServer結(jié)構(gòu)中。
save m n命令的實(shí)現(xiàn)原理:服務(wù)器每隔100毫秒執(zhí)行一次serverCron 函數(shù),serverCron 函數(shù)會(huì)遍歷save m n配置的保存條件判斷是否滿足。如果有一個(gè)條件滿足,就會(huì)觸發(fā)執(zhí)行 BGSAVE命令進(jìn)行快照保存。
對(duì)于每個(gè)save m n條件,只有以下兩個(gè)條件同時(shí)滿足才算滿足:
?當(dāng)前服務(wù)器時(shí)間減去lastsave時(shí)間戳大于m
?當(dāng)前dirty計(jì)數(shù)器的個(gè)數(shù)大于等于n
●用戶在客戶端執(zhí)行SAVE或BGSAVE命令時(shí)會(huì)觸發(fā)快照(手動(dòng)觸發(fā))。
●如果用戶定義了自動(dòng)快照條件則執(zhí)行FLUSHALL命令也會(huì)觸發(fā)快照。
當(dāng)執(zhí)行FLUSHALL 命令時(shí),會(huì)清空數(shù)據(jù)庫中的所有數(shù)據(jù)。如果用戶定義了自動(dòng)快照條件,則在使用FLUSHALL命令清空數(shù)據(jù)庫的過程中,就會(huì)觸發(fā)服務(wù)器執(zhí)行一次快照。
●如果用戶為Redis設(shè)置了主從復(fù)制模式,從節(jié)點(diǎn)執(zhí)行全量復(fù)制操作,則主節(jié)點(diǎn)會(huì)執(zhí)
行BGSAVE命令,將生產(chǎn)的RDB文件發(fā)送給從節(jié)點(diǎn)完成快照操作。
2.快照的實(shí)現(xiàn)過程
(1)Redis調(diào)用執(zhí)行fork函數(shù)復(fù)制一份當(dāng)前進(jìn)程(父進(jìn)程)的副本(子進(jìn)程),也就是同時(shí)擁有父進(jìn)程和子進(jìn)程。
(2)父進(jìn)程與子進(jìn)程各自分工,父進(jìn)程繼續(xù)處理來自客戶端的命令請(qǐng)求,而子進(jìn)程則將內(nèi)存中的數(shù)據(jù)寫到硬盤上的一個(gè)臨時(shí)RDB文件中。
(3)當(dāng)子進(jìn)程把所有數(shù)據(jù)寫完后,也就表示快照生成完畢,此時(shí)舊的RDB文件將會(huì)被這個(gè)臨時(shí)RDB文件替換,這個(gè)舊的RDB文件也會(huì)被刪除。這個(gè)過程就是一次快照的實(shí)現(xiàn)過程。
當(dāng)Redis調(diào)用執(zhí)行fork函數(shù)時(shí),操作系統(tǒng)會(huì)使用寫時(shí)復(fù)制策略。也就是在執(zhí)行fork函數(shù)的過程中,父、子進(jìn)程共享同一內(nèi)存數(shù)據(jù),當(dāng)父進(jìn)程要修改某個(gè)數(shù)據(jù)時(shí)(執(zhí)行一條寫命令),操作系統(tǒng)會(huì)將這個(gè)共享內(nèi)存數(shù)據(jù)另外復(fù)制一份給子進(jìn)程使用,以此來保證子進(jìn)程的正確運(yùn)行。因此新的RDB文件存儲(chǔ)的是執(zhí)行fork函數(shù)過程中的內(nèi)存數(shù)據(jù)。
寫時(shí)復(fù)制策略也保證了在執(zhí)行fork函數(shù)的過程中生成的兩份內(nèi)存副本在內(nèi)存中的占用量不會(huì)增加一倍。但是在進(jìn)行快照的過程中,如果寫操作比較多就會(huì)造成fork函數(shù)執(zhí)行前后數(shù)據(jù)差異較大,此時(shí)會(huì)使得內(nèi)存使用量變大。因?yàn)閮?nèi)存中不僅保存了當(dāng)前數(shù)據(jù)庫數(shù)據(jù),還會(huì)保存fork過程中的內(nèi)存數(shù)據(jù)。
在進(jìn)行快照生成的過程中,Redis不會(huì)修改RDB文件。只有當(dāng)快照生成后,舊的RDB 文件才會(huì)被臨時(shí)RDB文件替換,同時(shí)舊的RDB文件會(huì)被刪除。在整個(gè)過程中,RDB文件是完整的,因此我們可以使用RDB文件來實(shí)現(xiàn)Redis數(shù)據(jù)庫的備份。
10.2.2 RDB文件
在默認(rèn)情況下,Redis將數(shù)據(jù)庫快照保存在名為dump.rdb的文件中,這個(gè)文件被稱為 RDB文件,它是一個(gè)經(jīng)過壓縮的二進(jìn)制文件。使用RDB文件可以還原生成RDB文件的數(shù)據(jù)庫狀態(tài),也可以備份數(shù)據(jù)庫數(shù)據(jù)。
RDB文件的存儲(chǔ)路徑可以在啟動(dòng)前配置,也可以通過命令來直接修改。
配置文件配置:在Redis的配置文件redis.conf文件中,dir用于指定RDB文件、AOF 文件所在的目錄,默認(rèn)存放在Redis根目錄下,dbfilename 用于指定文件名稱。
命令修改:在Redis服務(wù)器啟動(dòng)后,也可以通過命令來修改RDB文件的存儲(chǔ)路徑,命令格式如下:
CONFIG SET dir {文件路徑}
CONFIG SET dbfilename {新文件名}
RDB文件結(jié)構(gòu)如圖所示:

在RDB文件結(jié)構(gòu)中,通常使用大寫字母表示常量,使用小寫字母表示變量和數(shù)據(jù)。
RDB文件主要由圖中的幾個(gè)常量和變量組成,具體說明如下:
● REDIS 常量:該常量位于RDB文件的頭部,它保存著“REDIS”5個(gè)字符,它的長(zhǎng)度是5字節(jié)。在Redis服務(wù)器啟動(dòng)加載文件時(shí),程序會(huì)根據(jù)這5個(gè)字符判斷加載的文件是不是RDB文件。
● db_version常量:該常量用于記錄RDB文件的版本號(hào),它的值是一個(gè)用字符串表示的整數(shù),占4字節(jié),注意區(qū)分它不是 Redis 的版本號(hào)。
● databases 數(shù)據(jù):它包含0個(gè)或多個(gè)數(shù)據(jù)庫以及各個(gè)數(shù)據(jù)庫中的鍵值對(duì)數(shù)據(jù)。
如果它包含0個(gè)數(shù)據(jù)庫,也就是服務(wù)器的數(shù)據(jù)庫狀態(tài)為空,那么databases也是空的,其長(zhǎng)度為0字節(jié);如果它包含多個(gè)數(shù)據(jù)庫,也就是服務(wù)器的數(shù)據(jù)庫狀態(tài)不為空,那么 databases不為空,根據(jù)它所保存的鍵值對(duì)的數(shù)量、類型和內(nèi)容不同,其長(zhǎng)度也是不一樣的。
如果databases不為空,則RDB文件結(jié)構(gòu)如圖所示:
[圖片上傳失敗...(image-49ee8d-1627917286542)]
其中,SELECTDB是一個(gè)常量,表示其后的數(shù)據(jù)庫編號(hào),這里的0和1是數(shù)據(jù)庫編號(hào)。
pairs數(shù)據(jù):它存儲(chǔ)了具體的鍵值對(duì)信息,包括鍵(key)、值(value)、數(shù)據(jù)類型、內(nèi)部編碼、過期信息、壓縮信息等。
SELECT 0 pairs表示0號(hào)數(shù)據(jù)庫;SELECT 1 pairs表示1號(hào)數(shù)據(jù)庫。當(dāng)數(shù)據(jù)庫中有鍵值對(duì)時(shí),RDB文件才會(huì)記錄該數(shù)據(jù)庫的信息;而如果數(shù)據(jù)庫中沒有鍵值對(duì),這一部分就會(huì)被RDB文件省略。
● EOF 常量:該常量是一個(gè)結(jié)束標(biāo)志,它標(biāo)志著 RDB 文件的正文內(nèi)容結(jié)束,其長(zhǎng)度為 1 字節(jié)。在加載 RDB 文件時(shí),如果遇到 EOF 常量,則表示數(shù)據(jù)庫中的所有鍵值對(duì)都已經(jīng)加載完畢。
● check_sum 變量:該變量用于保存一個(gè)校驗(yàn)和,這個(gè)校驗(yàn)和是通過對(duì) REDIS、 db_version、databases、EOF 4 部分的內(nèi)容進(jìn)行計(jì)算得出的,是一個(gè)無符號(hào)整數(shù),其長(zhǎng)度為 8 字節(jié)。
當(dāng)服務(wù)器加載 RDB 文件時(shí),會(huì)將 check_sum 變量中保存的校驗(yàn)和與加載數(shù)據(jù)時(shí)所計(jì)算出來的校驗(yàn)和進(jìn)行比對(duì),從而判斷加載的 RDB 文件是否被損壞,或者是否有錯(cuò)誤。
在默認(rèn)情況下,Redis 服務(wù)器會(huì)自動(dòng)對(duì) RDB 文件進(jìn)行壓縮。在 Redis 配置文件 redis.conf 中,默認(rèn)開啟壓縮。配置如下:
rdbcompression yes #默認(rèn)為開啟壓縮
如果不想開啟壓縮,則可以將 yes 值改為 no。也可以通過命令來修改,命令格式如下:
CONFIG SET rdbcompression no
在默認(rèn)情況下,Redis 采用 LZF 算法進(jìn)行 RDB 文件壓縮。在壓縮 RDB 文件時(shí),不要誤認(rèn)為是壓縮整個(gè) RDB 文件。實(shí)際上,對(duì) RDB 文件的壓縮只是針對(duì)數(shù)據(jù)庫中的字符串進(jìn)行的,并且只有當(dāng)字符串達(dá)到一定長(zhǎng)度(20 字節(jié))時(shí)才會(huì)進(jìn)行壓縮。
10.2.3 RDB文件的創(chuàng)建與加載
RDB文件可以使用命令直接生成。在 Redis 中,有SAVE和BGSAVE命令可以生成RDB文件。
在執(zhí)行 SAVE 命令的過程中,會(huì)阻塞Redis服務(wù)器進(jìn)程,此時(shí)Redis服務(wù)器將不能繼續(xù)執(zhí)行其他命令請(qǐng)求,直到 RDB 文件創(chuàng)建完畢為止。
執(zhí)行 SAVE 命令:
127.0.0.1:6379>SAVE
OK
返回 OK 表示 RDB 文件保存成功。
在執(zhí)行 BGSAVE 命令的過程中,BGSAVE 命令會(huì)派生出一個(gè)子進(jìn)程,交由子進(jìn)程將內(nèi)存中的數(shù)據(jù)保存到硬盤中,創(chuàng)建 RDB 文件;而 BGSAVE 命令的父進(jìn)程可以繼續(xù)處理來自客戶端的命令請(qǐng)求。
執(zhí)行 BGSAVE 命令:
127.0.0.1:6379>BGSAVE
Background saving started
返回 Background saving started 信息,但我們并不能確定 BGSAVE 命令是否已經(jīng)成功執(zhí)行,此時(shí)可以使用 LASTSAVE 命令來查看相關(guān)信息。
執(zhí)行 LASTSAVE 命令:
127.0.0.1:6379> LASTSAVE
(integer) 1531998138
返回一個(gè) UNIX 格式的時(shí)間戳,表示 近一次 Redis 成功將數(shù)據(jù)保存到硬盤中的時(shí)間。
其實(shí),真正創(chuàng)建 RDB 文件的不是 SAVE 或 BGSAVE 命令,而是 Redis 中的 rdb.c/rdbSave 函數(shù)。在執(zhí)行 SAVE 或 BGSAVE 命令后,會(huì)以不同的方式調(diào)用執(zhí)行這個(gè)函數(shù),進(jìn)而完成
RDB 文件的創(chuàng)建。
RDB 文件的創(chuàng)建可用于在啟動(dòng) Redis 服務(wù)器的時(shí)候恢復(fù)數(shù)據(jù)庫的狀態(tài),起到備份數(shù)據(jù)庫的作用。RDB 文件只有在啟動(dòng)服務(wù)器的時(shí)候才會(huì)被加載。當(dāng)啟動(dòng)服務(wù)器時(shí),它會(huì)檢查 RDB 文件是否存在,如果存在,就會(huì)自動(dòng)加載 RDB 文件。除此之外,RDB 文件不會(huì)被加載,因?yàn)?Redis 中沒有提供用于加載 RDB 文件的命令。
在啟動(dòng) Redis 時(shí),部分日志信息如圖 11.6 所示。
[圖片上傳失敗...(image-8df838-1627917286535)]
其中的信息 DB loaded from disk: 0.000 seconds 就是服務(wù)器在加載完 RDB 文件之后打印的,這里的時(shí)間為 0.000,是因?yàn)?RDB 文件過小,加載幾乎不耗費(fèi)時(shí)間。
在啟動(dòng) Redis 服務(wù)器的時(shí)候,到底是先加載 AOF 文件,還是先加載 RDB 文件呢?
在通常情況下,AOF 文件的更新頻率比 RDB 文件的更新頻率高得多,服務(wù)器每執(zhí)行一條寫命令,就會(huì)更新一次 AOF 文件。
其實(shí),在啟動(dòng) Redis 服務(wù)器的時(shí)候,會(huì)執(zhí)行一個(gè)加載程序,這個(gè)加載程序會(huì)根據(jù) Redis 配置文件中是否開啟了 AOF 持久化,來判斷是加載 AOF 文件還是加載 RDB 文件。
● 如果在 Redis 配置文件中開啟了 AOF 持久化(appendonly yes),那么在啟動(dòng)服務(wù)器的時(shí)候會(huì)優(yōu)先加載 AOF 文件來還原數(shù)據(jù)庫狀態(tài)。
● 如果在 Redis 配置文件中關(guān)閉了 AOF 持久化(appendonly no),那么在啟動(dòng)服務(wù)器的時(shí)候會(huì)優(yōu)先加載 RDB 文件來還原數(shù)據(jù)庫狀態(tài)。
加載 RDB 文件的實(shí)際工作由 rdb.c/rdbLoad 函數(shù)完成。
服務(wù)器啟動(dòng)加載 AOF 文件或 RDB 文件的過程如圖 11.7 所示。
[圖片上傳失敗...(image-f4dd3-1627917286540)]
圖 11.7 服務(wù)器啟動(dòng)加載 AOF 文件或 RDB 文件的過程
10.3.4 創(chuàng)建與加載RDB文件時(shí)服務(wù)器的狀態(tài)
在創(chuàng)建文件時(shí),服務(wù)器的狀態(tài)具體如下:
當(dāng)執(zhí)行 SAVE 命令的時(shí)候,將會(huì)阻塞 Redis 服務(wù)器,客戶端發(fā)送過來的命令請(qǐng)求將會(huì)被拒絕執(zhí)行。只有當(dāng) SAVE 命令執(zhí)行結(jié)束之后,服務(wù)器才能再次接收并執(zhí)行來自客戶端的命令請(qǐng)求。
當(dāng)執(zhí)行 BGSAVE 命令的時(shí)候,將會(huì)啟動(dòng)一個(gè)子進(jìn)程來創(chuàng)建并保存 RDB 文件,在子進(jìn)程創(chuàng)建 RDB 文件的過程中,父進(jìn)程仍然可以處理來自客戶端的命令請(qǐng)求。
如果在服務(wù)器執(zhí)行 BGSAVE 命令的過程中,客戶端向服務(wù)器發(fā)送過來的命令是 SAVE、
BGSAVE 或 BGREWRITEAOF,就會(huì)有不同的執(zhí)行策略,具體說明如下:
● 在執(zhí)行 BGSAVE 命令的過程中,如果客戶端發(fā)送過來的命令是 SAVE,那么該命令會(huì)被服務(wù)器拒絕執(zhí)行。因?yàn)樵趫?zhí)行 SAVE 命令的時(shí)候,服務(wù)器會(huì)被阻塞,這個(gè)時(shí)候子進(jìn)程也在執(zhí)行,就會(huì)造成父進(jìn)程和子進(jìn)程同時(shí)調(diào)用 rdbSave 函數(shù),產(chǎn)生競(jìng)爭(zhēng)條件。
為了避免這種情況的發(fā)生,服務(wù)器就會(huì)拒絕執(zhí)行 SAVE 命令。
● 在執(zhí)行 BGSAVE 命令的過程中,如果客戶端發(fā)送過來的命令是 BGSAVE,那么服務(wù)器會(huì)拒絕執(zhí)行 BGSAVE 命令。因?yàn)榉?wù)器如果同時(shí)執(zhí)行兩個(gè) BGSAVE 命令,則也會(huì)產(chǎn)生競(jìng)爭(zhēng)條件。
● 在執(zhí)行 BGSAVE 命令的過程中,如果客戶端發(fā)送過來的命令是 BGREWRITEAOF,那么這個(gè)命令會(huì)推遲到 BGSAVE 命令執(zhí)行完成之后才會(huì)被執(zhí)行。相反地,如果在執(zhí)行 BGREWRITEAOF 命令的過程中,客戶端發(fā)送過來的命令是 BGSAVE,那么服務(wù)器會(huì)拒絕執(zhí)行 BGSAVE 命令。BGSAVE 和 BGREWRITEAOF 命令都是采用子進(jìn)程來完成任務(wù)的,所以這兩個(gè)命令不能同時(shí)執(zhí)行。
服務(wù)器在加載 RDB 文件時(shí),會(huì)一直處于阻塞狀態(tài),直到 RDB 文件加載完畢,才會(huì)變?yōu)檫\(yùn)行狀態(tài)。
使用 INFO PERSISTENCE 命令來查看 RDB 持久化的相關(guān)狀態(tài),操作如下:
127.0.0.1:6379> INFO PERSISTENCE
Persistence loading:0 rdb_changes_since_last_save:12 rdb_bgsave_in_progress:0 rdb_last_save_time:1541692112 rdb_last_bgsave_status:ok rdb_last_bgsave_time_sec:-1 rdb_current_bgsave_time_sec:-1 rdb_last_cow_size:0
10.3.5 RDB 持久化的配置
RDB 持久化的配置具體如下。
● save m n:表示在時(shí)間 m 內(nèi)被修改的鍵的個(gè)數(shù)大于 n 時(shí),會(huì)觸發(fā) BGSAVE 命令的執(zhí)行。它是 BGSAVE 命令自動(dòng)觸發(fā)的條件;如果沒有設(shè)置該配置,則表示自動(dòng)的 RDB 持久化被關(guān)閉。
● stop-writes-on-bgsave-error yes:當(dāng)執(zhí)行 BGSAVE 命令出現(xiàn)錯(cuò)誤時(shí),Redis 是否終止執(zhí)行寫命令。參數(shù)的值默認(rèn)被設(shè)置為 yes,表示當(dāng)硬盤出現(xiàn)問題時(shí),服務(wù)器可以及時(shí)發(fā)現(xiàn),及時(shí)避免大量數(shù)據(jù)丟失;當(dāng)設(shè)置為 no 時(shí),就算執(zhí)行 BGSAVE 命令發(fā)生錯(cuò)誤,服務(wù)器也會(huì)繼續(xù)執(zhí)行寫命令;當(dāng)對(duì) Redis 服務(wù)器的系統(tǒng)設(shè)置了監(jiān)控時(shí),建議將該參數(shù)值設(shè)置為 no。
● rdbcompression yes:是否開啟 RDB 壓縮文件,默認(rèn)為 yes 表示開啟,不開啟則設(shè)置為 no。
● rdbchecksum yes:是否開啟 RDB 文件的校驗(yàn),在服務(wù)器進(jìn)行 RDB 文件的寫入與讀取時(shí)會(huì)用到它。默認(rèn)設(shè)置為 yes。如果將它設(shè)置為 no,則在服務(wù)器對(duì) RDB 文件進(jìn)行寫入與讀取時(shí),可以提升性能,但是無法確定 RDB 文件是否已經(jīng)被損壞。
● dbfilename dump.rdb:用于設(shè)置 RDB 文件名,可以通過命令來修改它,命令格式如下:
CONFIG SET dbfilename RDB文件名
● dir ./:RDB 文件和 AOF 文件所在目錄,默認(rèn)為 Redis 的根目錄。
10.3.6 RDB 持久化的優(yōu)劣
RDB 持久化具有以下優(yōu)點(diǎn):
● RDB 文件是一個(gè)經(jīng)過壓縮的二進(jìn)制文件,文件緊湊,體積較小,非常適用于進(jìn)行數(shù)據(jù)庫數(shù)據(jù)備份。
● RDB 持久化適用于災(zāi)難恢復(fù),而且恢復(fù)數(shù)據(jù)時(shí)的速度要快于 AOF 持久化。
● Redis 采用 RDB 持久化可以很大程度地提升性能。父進(jìn)程在保存 RDB 文件時(shí)會(huì)啟動(dòng)一個(gè)子進(jìn)程,將所有與保存相關(guān)的功能交由子進(jìn)程處理,而父進(jìn)程可以繼續(xù)處理其他相關(guān)的操作。
RDB 持久化具有以下缺點(diǎn):
● 在服務(wù)器出現(xiàn)故障時(shí),如果沒有觸發(fā) RDB 快照?qǐng)?zhí)行,那么它可能會(huì)丟失大量數(shù)據(jù)。
RDB 快照的持久化方式?jīng)Q定了必然做不到實(shí)時(shí)持久化,會(huì)存在大量數(shù)據(jù)丟失。
● 當(dāng)數(shù)據(jù)量非常龐大時(shí),在保存 RDB 文件的時(shí)候,服務(wù)器會(huì)啟動(dòng)一個(gè)子進(jìn)程來完成相關(guān)的保存操作。這項(xiàng)操作比較耗時(shí),將會(huì)占用太多 CPU 時(shí)間,從而影響服務(wù)器的性能。
● RDB 文件存在兼容性問題,老版本的 Redis 不支持新版本的 RDB 文件。
10.4 AOF持久化與RDB持久化抉擇
在實(shí)際的應(yīng)用場(chǎng)景中,由于存在各種風(fēng)險(xiǎn)因素,不知道服務(wù)器在什么時(shí)候會(huì)出現(xiàn)故障,也不知道在什么時(shí)候可能會(huì)斷電,又或者有其他一些意想不到的事情發(fā)生,這些事情的發(fā)生可能會(huì)導(dǎo)致Redis數(shù)據(jù)庫丟失大量數(shù)據(jù)造成經(jīng)濟(jì)損失。為了避免這些情況的發(fā)生,建議同時(shí)使用AOF持久化和RDB持久化以便最大限度地保證數(shù)據(jù)的持久化與安全性。
可以只使用RDB持久化,因?yàn)镽DB持久化能夠定時(shí)生成RDB快照,便于進(jìn)行數(shù)據(jù)庫數(shù)據(jù)備份,同時(shí)也能提高服務(wù)器的性能,而且RDB恢復(fù)數(shù)據(jù)的速度快于AOF恢復(fù)數(shù)據(jù)的速度,但必須承受如果服務(wù)器出現(xiàn)故障會(huì)丟失部分?jǐn)?shù)據(jù)的風(fēng)險(xiǎn)。
很多用戶只使用AOF持久化,但不推薦使用這種方式,因?yàn)锳OF持久化產(chǎn)生的AOF 文件體積較大,在恢復(fù)數(shù)據(jù)時(shí)會(huì)比較慢,會(huì)嚴(yán)重影響服務(wù)器的性能,在生成AOF文件時(shí)還可能會(huì)出現(xiàn)AOF程序Bug。
如果現(xiàn)在使用的是RDB持久化,想切換到AOF持久化,可以執(zhí)行以下幾步操作:
(1)備份RDB文件(dump.rdb)并將備份文件放到一個(gè)安全的地方
(2)在不重啟服務(wù)器的情況下,執(zhí)行以下命令:
CONFIG SET appendonly yes #開啟AOF持久化,服務(wù)器開始初始化AOF文件,此時(shí)Redis服務(wù)器會(huì)發(fā)生阻塞,直到AOF文件創(chuàng)建完畢為止,之后服務(wù)器才會(huì)繼續(xù)處理來自客戶端的命令請(qǐng)求,將寫命令追加到AOF文件中。同時(shí)需要手動(dòng)修改Redis的配置文件redis.conf中的appendonly參數(shù)值為yes,否則在下一次重啟的時(shí)候,并不會(huì)切換使用AOF持久化
CONFIG SET save " " #關(guān)閉RDB持久化。也可以不執(zhí)行該命令,同時(shí)使用RDB持久化和AOF持久化
(3)執(zhí)行上述命令后,需要檢查數(shù)據(jù)庫中的鍵數(shù)量有沒有改變,同時(shí)確保寫命令會(huì)被追加到AOF文件中