Redis持久化機制
Redis 的數據全部在內存里,如果突然宕機,數據就會全部丟失,因此必須有一種機制來保證 Redis 的數據不會因為故障而丟失,這種機制就是 Redis 的持久化機制。
什么是持久化?
就是把內存里的數據保存到硬盤上。
必須使用數據持久化嗎?
Redis的數據持久化機制是可以關閉的。如果你只把Redis作為緩存服務使用,Redis中存儲的所有數據都不是該數據的主體而僅僅是同步過來的備份,那么可以關閉Redis的數據持久化機制。
但通常來說,仍然建議至少開啟RDB方式的數據持久化,因為:
①數據量不是非常大時,RDB方式的持久化幾乎不損耗Redis本身的性能,因為Redis父進程持久化時只需要fork一個子進程,這個子進程可以共享主進程的所有內存數據,子進程會去讀取主進程的內存數據,并把它們寫入RDB文件。
②Redis無論因為什么原因發送故障,重啟時能夠自動恢復到上一次RDB快照中記錄的數據(自動加載RDB文件)。這省去了手工從其他數據源(如數據庫)同步數據的過程,而且要比其他任何的數據恢復方式都要快。
③服務器的硬盤都是T級別的,幾個G的數據影響忽略不計。
Redis 不同于 Memcached 的很重要一點就是,Redis 支持持久化,而且支持三種不同的持久化策略。
1.RDB
Redis提供了兩個命令來生成 RDB 文件:
- save:在主進程中執行,會導致寫請求阻塞。
- bgsave:fork一個子進程,專門用于寫入 RDB 文件,避免了主進程的阻塞。
為了快照而阻塞寫請求,這是系統無法接受的,因此Redis借助操作系統提供的寫時復制技術(Copy-On-Write, COW),在執行快照的同時,正常處理寫操作。
Redis在執行持久化時,會fork出一個bgsave子進程,這個子進程可以共享主進程的所有內存數據,bgsave子進程運行后,會去讀取主進程的內存數據,并把它們寫入RDB文件。
有小伙伴問,為什么要fork一個子線程?
redis是單線程程序,若單線程同時在服務線上的請求還需要進行文件IO操作,這不僅影響性能而且還會阻塞線上業務,因此這里主進程fork出一個進程,fork出的這個進程去完成快照操作。
快照持久化是 Redis 默認采用的持久化方式,我們可以根據業務需求配置下面參數:
save 900 1 #每900秒(15分鐘)至少有1個key發生變化,Redis就會自動觸發BGSAVE命令創建快照。
save 300 10 #每300秒(5分鐘)至少有10個key發生變化,Redis就會自動觸發BGSAVE命令創建快照。
save 60 10000 #每60秒(1分鐘)至少有10000個key發生變化,Redis就會自動觸發BGSAVE命令創建快照。
key發生變化(key數據添加、修改、刪除)
觸發快照的幾種方式:
①服務器正常關閉時,會照一次快照 ./bin/redis-cli shutdown
②key滿足一定條件,會照一次快照(通過上述Redis.conf配置)
③通過BGSAVE命令(在redis中執行)手動觸發RDB快照保存
優點:
①RDB文件緊湊,體積小,網絡傳輸快,適合全量復制。
②與AOF方式相比,通過RDB文件恢復數據比較快更快。
③RDB最大化了Redis的性能,因為Redis父進程持久化時只需要fork一個子進程,這個子進程可以共享主進程的所有內存數據,子進程會去讀取主進程的內存數據,并把它們寫入RDB文件。
缺點:
①快照是定期生成的,所有在 Redis 故障時或多或少會丟失一部分數據。
②當數據量比較大時,fork 的過程是非常耗時的,fork 子進程時是會阻塞的,在這期間 Redis 是不能響應客戶端的請求的。
2.AOF
Redis會把每一個寫請求都記錄在一個日志文件里,在Redis重啟時,會把AOF文件中記錄的所有寫操作順序執行一遍,確保數據恢復到最新。
Redis 會在收到客戶端修改指令后,先進行參數校驗,如果沒問題,就立即將該指令文本存儲到 AOF 日志中,也就是先存到磁盤,然后再執行指令。這樣即使遇到突發宕機,已經存儲到 AOF 日志的指令進行重放一下就可以恢復到宕機前的狀態。
日志文件太大怎么辦?
AOF 日志在長期的運行過程中會變的很大,Redis重啟時需要加載 AOF 日志進行指令重放,此時這個過程就會非常耗時。 所以需要定期進行AOF 重寫,給 AOF 日志進行瘦身。
AOF如何重寫?
Redis 提供了 bgrewriteaof 指令用于對 AOF 日志進行瘦身。每次執行重寫時,主進程 fork 出一個bgrewriteaof 子進程,會把主進程的內存拷貝一份給 bgrewriteaof 子進程,對內存進行遍歷轉換成一系列 Redis 的操作指令,序列化到一個新的 AOF 日志文件中。序列化完畢后再將操作期間發生的增量 AOF 日志追加到這個新的 AOF 日志文件中,追加完畢后就立即替代舊的 AOF 日志文件了,瘦身工作就完成了。
Redis提供了AOF rewrite功能,可以重寫AOF文件,只保留能夠把數據恢復到最新狀態的最小寫操作集。
AOF 重寫可以通過bgrewriteaof命令(在redis里執行)觸發,也可以配置Redis定期自動進行:
## Redis在每次AOF rewrite時,會記錄完成rewrite后的AOF日志大小,當AOF日志大小在該基礎上增長了100%后,自動進行AOF rewrite。同時如果增長的大小沒有達到64mb,則不會進行rewrite。
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
AOF默認是關閉的,如果需要開啟,需要在redis.conf配置文件中配置:
appendonly yes
AOF提供三種fsync配置,always/everysec/no,通過配置項appendfsync指定,默認是everysec。
appendfsync always # 每寫入一條日志就進行一次fsync操作,數據安全性最高,但速度最慢(每次有數據修改發生時都會寫入AOF文)
appendfsync everysec # 折中的做法,交由后臺線程每秒fsync一次(每秒鐘同步一次,該策略為AOF的缺省策略)
appendfsync no # 不進行fsync,將flush文件的時機交給OS決定,速度最快(從不同步。高效但是數據不會被持久化)
優點:
①數據安全性高,可以根據業務需求配置fsync策略
②AOF文件易讀,可修改,在進行了某些錯誤的數據清除操作后,只要AOF文件沒有rewrite,就可以把AOF文件備份出來,把錯誤命令刪除,然后恢復數據
缺點:
①AOF方式生成的日志文件太大,即使通過AFO重寫,文件體積仍然很大
②數據恢復速度比RDB慢
3.混合持久化
如果我們采用 RDB 持久化會丟失一段時間數據。如果我們采用 AOF 持久化,AOF日志較大,重放比較慢。
Redis 4.0 為了解決這個問題,支持混合持久化。將 RDB 文件的內容和增量的 AOF 日志文件存在一起。
混合持久化同樣也是通過 bgrewriteaof 完成的,不同的是當開啟混合持久化時,fork出的子進程先將共享的內存副本全量的以 RDB 方式寫入 AOF 文件,然后在將重寫緩沖區的增量命令以 AOF 方式寫入到文件,寫入完成后通知主進程更新統計信息,并將新的含有RDB格式和 AOF 格式的 AOF 文件替換舊的的 AOF 文件。簡單的說:新的AOF文件前半段是RDB格式的全量數據后半段是AOF格式的增量數據。
于是在 Redis 重啟的時候,可以先加載 rdb 的內容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重啟效率因此大幅得到提升。
4.實戰經驗
快照需要fork子進程的方式進行的,它是一個比較耗資源的操作。(當數據量非常大時,fork會很耗時,需要大概幾百毫秒甚至1秒,fork時父進程是阻塞的,不能正常服務redis讀寫請求)
AOF 的 fsync 是一個耗時的 IO 操作,它會降低 Redis 性能,同時也會增加系統 IO 負擔
所以通常 Redis 的主節點是不會進行持久化操作,持久化操作主要在從節點進行。從節點是備份節點,沒有來自客戶端請求的壓力,它的操作系統資源往往比較充沛。
但是如果出現網絡分區,從節點長期連不上主節點,就會出現數據不一致的問題,特別是在網絡分區出現的情況下又不小心主節點宕機了,那么數據就會丟失,所以在生產環境要做好實時監控工作,保證網絡暢通或者能快速修復。另外還應該再增加一個從節點以降低網絡分區的概率,只要有一個從節點數據同步正常,數據也就不會輕易丟失。