前言
大家都知道Redis一個記憶體資料庫,它支援2種持久化方式:RDB(Snapshot 記憶體快照) ,AOF(append only file)。持久化功能将記憶體中的資料同步到磁盤來避免Redis發生異常導緻資料丢失的情況。當Redis執行個體重新開機時,即可利用之前持久化的檔案實作資料恢複。
接下來,本文介紹兩種持久化的運作機制和優缺點。
一 RDB
RDB是預設的持久化方式,按照一定的政策周期性的将記憶體中的資料生成快照儲存到磁盤。
每次快照持久化都是将記憶體資料完整寫入到磁盤一次,并不 是增量的隻同步髒資料。如果資料量大的話,而且寫操作比較多,必然會引起大量的磁盤io操作,可能會嚴重影響性能。
1.1快照持久化過程

1.2觸發機制
1.save 指令
當用戶端向Redis server發送save指令請求進行持久化時,由于Redis是用一個主線程來處理所有,save指令會阻塞Redis server處理其他用戶端的請求,直到資料同步完成。
2.bgsave指令
與save指令不同,bgsave是異步執行的,當執行bgsave指令之後,Redis主程序會fork 一個子程序将資料儲存到rdb檔案中,同步完資料之後,對原有檔案進行替換,然後通知主程序表示同步完成。
3.自動觸發
除了手動觸發RDB持久化,Redis内部還存在自動觸發機制,
在配置中集中配置 save m n
的方式,表示 m秒内資料集存在n次修改時,系統自動觸發bgsave 操作。
# 900s内至少達到一條寫指令
save 900 1
# 300s内至少達至10條寫指令
save 300 10
# 60s内至少達到10000條寫指令
save 60 10000
從節點執行全量複制操作,主節點自動執行bgsave 生成RDB檔案并發送給從節點
預設情況下執行 shutdown 指令時,如果沒有開啟AOF持久化功能,系統會自動執行bgsave指令。執行debug reload 指令重新加載Redis時,也會自動觸發save操作。
1.3相關參數
# 持久化 rdb檔案遇到問題時,主程序是否接受寫入,yes 表示停止寫入,如果是no 表示redis繼續提供服務。
stop-writes-on-bgsave-error yes
# 在進行快照鏡像時,是否進行壓縮。yes:壓縮,但是需要一些cpu的消耗。no:不壓縮,需要更多的磁盤空間。
rdbcompression yes
# 一個CRC64的校驗就被放在了檔案末尾,當存儲或者加載rbd檔案的時候會有一個10%左右的性能下降,為了達到性能的最大化,你可以關掉這個配置項。
rdbchecksum yes
# 快照的檔案名
dbfilename dump.rdb
# 存放快照的目錄
dir /var/lib/redis
1.4RDB的優缺點
優點
RDB檔案小,非常适合定時備份,用于災難恢複。
因為RDB檔案中直接存儲的是記憶體資料,而AOF檔案中存儲的是一條條指令,需要應用指令。Redis加載RDB檔案的速度比AOF快很多。
缺點
RDB持久化方式不能做到實時/秒級持久化。實時持久化要全量刷記憶體到磁盤,成本太高。每秒fork子程序也會阻塞主程序,影響性能。
RDB檔案是二進制檔案,随着Redis不斷疊代有多個rdb檔案的版本,不支援跨版本相容。老的Redis無法識别新的RDB檔案格式。
二 AOF
AOF(Append-only file)針對RDB的缺點做了優化,在使用AOF持久化方式時,Redis會将每一個收到的寫操作指令都通過Write函數追加到檔案最後,類似于MySQL的binlog。當Redis重新開機時會通過重新執行檔案中儲存的寫指令來在記憶體中重建整個資料庫的内容。
2.1 AOF持久化過程
1.用戶端發出 bgrewriteaof指令。
2.redis主程序fork子程序。
3.父程序繼續處理client請求,除了把寫指令寫入到原來的aof檔案中。同時把收到的寫指令緩存到 AOF重寫緩沖區。這樣就能保證如果子程序重寫失敗的話并不會出問題。
4.子程序根據記憶體快照,按照指令合并規則寫入到新AOF檔案中。
5.當子程序把記憶體快照寫入臨時檔案中後,子程序發信号通知父程序。然後父程序把緩存的寫指令也寫入到臨時檔案。
6.現在父程序可以使用臨時檔案替換老的aof檔案,并重命名,後面收到的寫指令也開始往新的aof檔案中追加。
2.2 相關參數
# 是否開啟AOF,預設關閉
appendonly yes
# 指定 AOF 檔案名
appendfilename appendonly.aof
# Redis支援三種刷寫模式:
# appendfsync always #每次收到寫指令就立即強制寫入磁盤,類似MySQL的sync_binlog=1,是最安全的。但該模式下速度也是最慢的,一般不推薦使用。
appendfsync everysec #每秒鐘強制寫入磁盤一次,在性能和持久化方面做平衡,推薦該方式。
# appendfsync no #完全依賴OS的寫入,一般為30秒左右一次,性能最好但是持久化最沒有保證,不推薦。
#在日志重寫時,不進行指令追加操作,而隻是将其放在緩沖區裡,避免與指令的追加造成DISK IO上的沖突。
#設定為yes表示rewrite期間對新寫操作不fsync,暫時存在記憶體中,等rewrite完成後再寫入,預設為no,建議yes
no-appendfsync-on-rewrite yes
#目前AOF檔案大小是上次日志重寫得到AOF檔案大小的二倍時,自動啟動新的日志重寫過程。
auto-aof-rewrite-percentage 100
#目前AOF檔案啟動新的日志重寫過程的最小值,避免剛剛啟動Reids時由于檔案尺寸較小導緻頻繁的重寫。
auto-aof-rewrite-min-size 64mb
2.3 日志重寫
AOF機制将用戶端的每一個寫操作都追加到aof檔案末尾,比如将一個key多次執行incr,set指令,會寫入多次指令到aof檔案,aof檔案會越來越大,部分核心業務每天的寫入量有幾十G的大小。
incr k1 1
set k2 a
set k2 b
incr k1 2
incr k1 3
set k2 c
del k3
...
incr k1 100
恢複Redis執行個體時,加載非常大的aof檔案耗時會很長。為了解決這個問題,Redis 支援aof檔案重寫--把Redis程序内的資料轉化為寫指令同步到新AOF檔案中的過程。通過重寫,可以生成一個最小的指令集合。比如上面的幾個指令可以合并為
incr k1 100
set k2 c
寫入資料的規則
1 程序内過期的資料不用在寫入
2 舊AOF檔案含有的無效指令 del k1, set a 1 ,set a 2 .重寫使用程序内的資料直接生成,aof檔案就保留最新的指令集合。
3 多條指令可以合并為一個指令,為了防止單個指令過大造成用戶端緩沖區溢出,對于list,set,hash,zset 等類型的操作,以64個元素為界拆分為多條。
觸發機制
1.手動觸發 執行bgrewriteaof指令。
2.根據配置自動觸發
auto-aof-rewrite-min-size 表示運作AOF重寫是檔案最小的大小。預設64M,小于64M就會不自動重寫了。
auto-aof-rewrite-percentage 表示目前檔案大小
aof_current_size
/ 上一次重寫後AOF檔案大小
aof_base_size
的比值。
自動觸發時機 :
aof_current_size
>
auto-aof-rewrite-min-size
&& (
aof_current_size
-
aof_base_size
) /
aof_base_size
>=
auto-aof-rewrite-percentage
三 RDB VS AOF 對比
具體使用哪種持久化方式 ,下面是來自官方的建議:
通常,如果你要想提供很高的資料保障性,那麼建議你同時使用兩種持久化方式。如果你可以接受災難帶來的幾分鐘的資料丢失,那麼你可以僅使用RDB。很多使用者僅使用了AOF,但是我們建議,既然RDB可以時不時的給資料做個完整的快照,并且提供更快的重新開機,是以最好還是也使用RDB。
生産上的執行個體大多不會是單點,而是主從,也有利用slave作為持久化方式,同時滿足HA的需求。讀者朋友可以分享一下各自遇到的和 redis 持久化相關的問題。
最後推薦一本書 ,非常适合運維朋友學習。
-The End-
本公衆号長期關注于資料庫技術以及性能優化,故障案例分析,資料庫運維技術知識分享,個人成長和自我管理等主題,歡迎掃碼關注。