深入探索Redis持久化原理
Redis是一個內存數(shù)據(jù)庫,為了保證數(shù)據(jù)的持久化,redis提供了兩種持久化方式RDB和AOF,下面我們就分別來看下這兩種持久化方式的實現(xiàn)原理。
RDB(默認)
RDB是通過快照方式完成的,當滿足一定條件時,redis會自動將內存中的數(shù)據(jù)持久化到磁盤。
觸發(fā)快照的時機
- 符合自定義配置的快照規(guī)則。(在redis.conf中配置,下面會詳細介紹)
- 執(zhí)行save或者bgsave命令
- 執(zhí)行flushall命令
- 執(zhí)行主從復制操作(第一次)
原理圖

- 在快照進行的過程中,也就是生成文件的過程中,不會對原有的rdb文件進行修改,直到快照生成完畢,直接將老的替換成新的,保證rdb文件任何時刻都是完整的。
- 我們可以通過定時備份rdb文件來實現(xiàn)redis數(shù)據(jù)的備份,rdb是壓縮的二進制文件,占用空間小,有利于傳輸
###快照設置規(guī)則 save {多少秒內} {數(shù)據(jù)變化了多少}
例如:save 100 1:在100秒內,至少有一個鍵被修改就進行快照;save 200 4:在200秒內,至少4個鍵被修改就進行快照。
RDB的優(yōu)缺點
- 缺點:使用RDB進行持久化,在redis突然異常退出的時候,會丟失最后一次快照之后的數(shù)據(jù)。但是,可以根據(jù)組合設置自動快照的方式,降低數(shù)據(jù)損失,確保在接受范圍內。如果數(shù)據(jù)較為重要,可以使用AOF方式
- 優(yōu)點:使用RDB方式可以最大化redis性能,在快照過程中,我們可以看到主進程只需要fork出一個子進程即可,剩下的工作全部由子進程完成,父進程無需進行任何的磁盤I/O操作。但是,如果數(shù)據(jù)集較大,在fork子進程的時候比較耗時,會導致redis在一段時間內停止處理請求。
AOF方式
默認情況下,redis是沒有開啟AOF(append only file)的。在開始AOF之后,redis每接收到一條更改redis數(shù)據(jù)的命令,就會將該命令寫入硬盤中的AOF文件。很明顯,該過程會降低redis的性能,但大部分情況下是能夠接受的,同時,使用性能較好的硬盤可以提高AOF性能
AOF配置方式
# 開啟appendonly參數(shù)appendonly true# 設置AOF文件位置dir ./# 設置AOF文件名稱,默認是appendonly.aofappendfilename appendonly.aof
原理圖

RESP協(xié)議
Redis客戶端使用RESP協(xié)議與Redis服務端進行通信,該協(xié)議是專門為redis設計的,但是也可以用于其他C-S項目。
- 間隔符號:在linux中是\r\n,在windows中是\n
- 簡單字符串以'+'開頭
- 錯誤Errors,以'-'開頭
- 整數(shù)類型Integer,以':'開頭
- 大字符串以'$'開頭
- 數(shù)組類型Arrays,以'*'開頭
AOF原理說明
redis通過將所有的寫入命令記錄到AOF文件中,來持久化數(shù)據(jù)。而將命令記錄到AOF文件的過程,可以分成三個階段:
- 命令傳播
- 緩存追加
- 文件寫入和保存
命令傳播
redis將執(zhí)行完的命令,命令參數(shù),命令參數(shù)格個數(shù)等內容發(fā)送到AOF程序。當redis客戶端執(zhí)行命令的時候,通過連接,將協(xié)議文本發(fā)送到redis,redis接收到協(xié)議文本之后,根據(jù)內容,選擇適當?shù)拿詈瘮?shù),將協(xié)議文本轉換成Redis字符串對象,在命令函數(shù)執(zhí)行完成后,將命令參數(shù)發(fā)送到AOF程序。
緩存追加
AOF程序接收到命令參數(shù)之后,會將其從字符串對象轉換成協(xié)議內容,再將協(xié)議內容追加到AOF緩存中。AOF緩存是在redisServer結構的aof_buf中,新的內容會被追加到aof_buf末尾。aof_buf保持著所有未被寫入到AOF文件的協(xié)議文本。
文件寫入和保存
將AOF緩存內容寫入到AOF文件,并保持到磁盤。 當服務器的常規(guī)函數(shù)被執(zhí)行,或者事件處理器被執(zhí)行的時候,flushAppendOnlyFile函數(shù)將會被執(zhí)行。會有以下兩個過程。
- WRITE:將AOF緩存中的內容寫入到AOF文件
- SAVE:調用fsync或者fdatasync函數(shù),將AOF文件保存到磁盤中
AOF一共有三種保存模式
- AOF_FSYNC_NO:不保存。在這種模式下,每次調用flushAppendOnlyFile函數(shù),write會被執(zhí)行,而save會被忽略。而save只有在以下三種條件下會觸發(fā),redis關閉,aof功能關閉,系統(tǒng)的寫緩存被刷新(可能是緩存滿了,或者定期執(zhí)行保持操作)。這三種情況下執(zhí)行save操作會引起redis主線程的阻塞。
- AOF_FSYNC_EVERYSEC(默認):每秒保存一次。save每秒被執(zhí)行一次,但是save由后臺子線程完成,不會導致redis主線程阻塞。
- AOF_FSYNC_ALWAYS:每執(zhí)行一個命令保存一次。這種情況下,每執(zhí)行一個命令write和save都會被執(zhí)行,而且save操作由主線程完成,會導致redis的阻塞。安全性較高,性能較差,因此不推薦使用。
AOF的文件優(yōu)化
Redis可以在AOF文件過大的時候,在后臺(子進程)對AOF文件進行重寫。重寫之后的新文件,包含恢復當前數(shù)據(jù)集所需的最小命令集合。重寫,并不會對AOF文件進行讀取和寫入,針對的是數(shù)據(jù)庫中的當前鍵。
優(yōu)化前set s1 1set s1 2set s1 3優(yōu)化后set s1 3
AOF文件優(yōu)化原理
AOF的重寫是通過子進程實現(xiàn)的,因此,主線程是繼續(xù)工作的,有可能對新的數(shù)據(jù)進行修改,有可能會導致數(shù)據(jù)庫的數(shù)據(jù)和重寫之后的數(shù)據(jù)不一致。redis通過增加一個AOF重寫緩存來解決這個問題,當fork出子進程之后,新的命令不僅會追加到現(xiàn)有的AOF文件中,還會添加一份數(shù)據(jù)到這個緩存當中。

重寫過程分析(需要保證從寫的操作是絕對安全的)
Redis創(chuàng)建新的AOF文件之后,會繼續(xù)將命令添加到原有的AOF文件中,即使數(shù)據(jù)庫突然宕機了,原有的AOF文件和文件內容也不會有損失。而當新的AOF文件創(chuàng)建完畢之后,會直接把舊的替換掉,往新的AOF文件中添加命令。
當子進程在進行重寫時,主進程會完成下列工作
- 處理請求,將新的命令繼續(xù)添加到AOF文件中,同時將命令添加到AOF重寫緩存中。保證數(shù)據(jù)的一致行,避免出現(xiàn)數(shù)據(jù)丟失。
- 當子進程重寫完畢之后會向主進程發(fā)送一個完成信號,這時主進程會把重寫緩存中的內容添加到新的AOF文件中,這樣新的AOF文件,數(shù)據(jù)庫,舊的AOF文件的內容就完全一致了。然后對新的AOF文件改名,覆蓋原有的AOF文件。
這樣程序就完成了新舊AOF文件的替換工作。而當處理完成之后,主進程就會繼續(xù)接受請求。整個重寫過程中只有最后的緩存寫入和改名替換的操作會導致主進程阻塞,其他時候不會影響redis的正常工作,把AOF重寫對redis的性能影響降到最低。
優(yōu)化觸發(fā)條件
#當前的AOF文件超過上次重寫時AOF文件大小百分幾的時候進行重寫,如果沒有重寫過,則以啟動時AOF文件的大小為準auto-aof-rewrite-percentage 100#限制允許重寫的最小的AOF文件,也就是文件小于這個值的時候不需要進行重寫優(yōu)化auto-aof-rewrite-min-size 64mb