全是幹貨的技術号:
本文已收錄在github,歡迎 star/fork:
https://github.com/Wasabi1234/Java-Interview-Tutorial
Redis 對外提供資料通路服務時,使用的是常駐記憶體的資料。如果僅将資料存在記憶體,一旦當機重新開機,資料全部丢失。
1 持久化概論
1.1 什麼是持久化
redis所有資料保持在記憶體中,對資料的更新将異步地儲存到磁盤上。持久化主要是做災難恢複、資料恢複,可歸類到高可用。
比如你的Redis當機,你要做的事情是讓Redis變得可用,盡快變得可用!
重新開機Redis,盡快讓它對外提供服務,若你沒做資料備份,即使Redis啟動了,資料都沒了!可用什麼呢?
很可能說,大量的請求過來,緩存全部無法命中,在Redis裡根本找不到資料,這個時候就造成緩存雪崩,就會去MySQL資料庫去找,突然MySQL承接高并發,當機!
MySQL當機,你都沒法去找資料恢複到Redis裡面去,Redis的資料從哪兒來?就是從MySQL來的!
若你把Redis的持久化做好,備份和恢複方案也做到,那麼即使你的Redis故障,也可通過備份資料,快速恢複,一旦恢複立即對外提供服務
1.2 持久化方式
Redis提供了兩種持久化方式:
Redis RDB - 快照
RDB 按指定時間間隔執行資料集的時間點快照,類似于MySQL Dump。
Redis AOF - 指令日志
AOF 會記錄伺服器接收的每個寫操作,這些操作将在伺服器啟動時再次執行,以重建原始資料集。使用與Redis協定本身相同的格式記錄指令,并且僅采用
append-only
方式。當日志太大時,Redis可以在背景重寫日志。類似于MySQL Binlog、Hbase HLog。在Redis重新開機時,通過回放日志中的寫入指令來重構整個資料。
如果希望Redis僅作為純記憶體的緩存來用,亦可禁用RDB和AOF。
可以在同一執行個體中同時使用AOF和RDB。這種情況下,當Redis重新啟動時,AOF檔案将用于重建原始資料集,因為它可以保證是最完整的。
最重要的是了解RDB與AOF持久性之間的不同權衡。如果同時使用RDB和AOF兩種持久化機制,那麼在Redis重新開機時,會使用AOF來重新建構資料,因為AOF中的資料更加完整!
2 RDB - 全量寫入
Redis Server在有多db 中存儲的K.V可了解為Redis的一個狀态。當發生
寫操作
時,Redis就會從一個狀态切換到另外一個狀态。
基于全量的持久化就是在某個時刻,将Redis的所有資料持久化到硬碟中,形成一個快照。當Redis 重新開機時,通過加載最近一個快照資料,可以将 Redis 恢複至最近一次持久化狀态上。
2.1 觸發方式
save 指令
save 可以由用戶端顯示觸發,也可在redis shutdown 時觸發。
save本身是
單線程串行
方式執行,是以當資料量大時,可能會發生Redis Server的長時間卡頓。但其備份期間不會有其他指令執行,是以備份時期
資料的狀态始終是一緻性
的。
若存在老的RDB檔案,則新的會替換老的,時間複雜度O(N)。
bgsave
bgsave 也可由
- 用戶端顯式觸發
- 配置定時任務觸發
- 主從架構下由從節點觸發
bgsave指令在執行時,會
fork
一個子程序。子程序送出完成後,會立即給用戶端傳回響應,備份操作在背景異步執行,期間不會影響Redis的正常響應。
對于bgsave來說,當父程序Fork完子程序之後,異步任務會将
目前的記憶體狀态作為一個版本進行複制
在複制過程中産生的變更,不會反映在這次備份當中。
不用指令,而使用配置
在Redis的預設配置中,當滿足下面任一條件,會自動觸發 bgsave 的執行:
配置 | seconds | changes |
---|---|---|
save | 900 | 1 |
300 | 10 | |
60 | 10000 |
bgsave相對于save的優勢是
異步執行
,不影響後續指令執行。但Fork子程序,涉及父程序的記憶體複制,會增加伺服器記憶體開銷。
當記憶體開銷高到使用虛拟記憶體時,bgsave的Fork子程序會阻塞運作
,可能會造成秒級不可用。是以使用bgsave需要保證伺服器空閑記憶體足夠。
指令 | ||
---|---|---|
IO類型 | 同步 | 異步 |
是否阻塞 | 阻塞 | 非阻塞(在fork時阻塞) |
複雜度 | O(N) | |
優點 | 不會消耗額外記憶體 | 不阻塞用戶端指令 |
缺點 | 阻塞用戶端指令 | 需要fork子程序,記憶體開銷大 |
RDB 最佳配置
關閉自動RDB:
dbfilename dump-${port}.rdb
dir /redisDataPath
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
需要注意的觸發時機
- 主從複制時機的全量複制,master節點會執行bgsave
- debug reload
- shutdown
- flushDB 、 flushAll
RDB性質
- RDB是Redis記憶體到硬碟的快照,用于持久化
- save通常會阻塞Redis
- bgsave不會阻塞Redis,但會fork新程序
- save自動配置滿足任一就會被執行
RDB 優點
- RDB會生成多個資料檔案,每個檔案都代表了某時刻Redis中的所有資料,這種方式非常适合做冷備,可将這種完整資料檔案發送到雲伺服器存儲,比如ODPS分布式存儲,以預定好的備份政策來定期備份Redis中的資料
- RDB對Redis對外提供的讀寫服務,影響非常小,可讓Redis保持高性能,因為Redis主程序隻要fork一個子程序,讓子程序執行RDB
- 相對于AOF,直接基于RDB檔案重新開機和恢複Redis程序,更加快速
RDB缺點
- 耗時,O(n)
-
fork():耗記憶體,copy-on-write政策
RDB每次在fork子程序來執行RDB快照資料檔案生成的時候,如果資料檔案特别大,可能會導緻對用戶端提供的服務暫停數毫秒,或者甚至數秒
-
不可控,容易丢失資料
一般RDB每隔5分鐘,或者更長時間生成一次,若過程中Redis當機,就會丢失最近未持久化的資料
2.2 恢複流程
當Redis重新啟動時,會從本地磁盤加載之前持久化的檔案。當恢複完成之後,再受理後續的請求操作。
3 AOF(append only file)- 增量模式
RDB記錄的是每個狀态的全量資料,AOF記錄的則是每條寫指令的記錄,通過所有寫指令的執行,最後恢複出最終的資料狀态。
- 其檔案的生成如下:
3.1 寫入流程
AOF的三種政策
always
- 每次重新整理緩沖區,都會同步觸發同步操作。因為每次的寫操作都會觸發同步,是以該政策會降低Redis的吞吐量,但該 模式會擁有最高的容錯能力。
every second
- 每秒異步的觸發同步操作,為Redis的
。預設配置
no
- 由作業系統決定何時同步,該方式下Redis無法決定何時落地,是以不可控。
對比
everysec | |||
---|---|---|---|
不丢失資料 | 每秒1次fsync,丢1秒資料 | 無需設定 | |
IO開銷大,一般的STAT盤隻有幾百TPS | 丢1秒資料 | 不可控 |
3.2 回放流程
AOF的回放時機也是在
機器啟動時
,
一旦存在AOF,Redis就會選擇增量回放
因為增量持久化是持續的寫盤,相比于全量持久化,資料更加完整。回放過程就是将AOF中存放的指令,重新執行一遍。完成後再繼續接收用戶端新指令。
AOF模式的優化重寫
随着Redis 持續的運作,會有大量的增量資料append 到AOF 檔案中。為了減小硬碟存儲和加快恢複速度,Redis 通過rewrite 機制合并曆史AOF 記錄。如下所示:
原生 AOF
set hello world
set hello java
set hello hehe
incr counter
incr counter
rpush mylist a
rpush mylist b
rpush mylist c
過期資料
AOF 重寫
set hello hehe
set counter 2
rpush mylist a b c
AOF重寫的作用
- 減少硬碟占用量
- 加速恢複速度
3.3 AOF重寫實作兩種方式
bgrewriteaof
AOF 重寫配置
配置項
- AOF檔案增長率 / AOF檔案重寫需要的大小
- AOF目前尺寸(機關:位元組)
-
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
3.4 AOF 重寫流程
修改配置檔案
appendonly yes
appendfilename "appendonly-$(port).aof"
appendfsync everysec
dir /opt/soft/redis/data
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
no-appendfsync-on-rewrite yes
AOF的優點
-
更好避免資料丢失
一般AOF每隔1s,通過子程序執行一次fsync,最多丢1s資料
-
append-only
模式追加寫
是以沒有任何磁盤尋址的開銷,寫入性能高,且檔案不易破損,即使檔案尾部破損,也易修複
-
日志檔案即使過大,出現背景重寫操作,也不會影響用戶端的讀寫
因為在rewrite log時,會壓縮其中的指令,建立出一份需要恢複資料的最小日志。在建立新日志時,舊日志檔案還是照常寫入。當新的merge後的日志檔案準備好時,再交換新舊日志檔案即可!
-
指令通過非常可讀的方式記錄
該特性非常适合做災難性誤删除操作的緊急恢複。
比如某人不小心用flushall指令清空了所有資料,隻要這個時候背景rewrite還沒有發生,可立即拷貝AOF檔案,将最後一條flushall指令給删了,然後再将該AOF檔案放回去,就可通過恢複機制,自動恢複所有資料
2.2.2 AOF的缺點
- 對于同一份資料,AOF日志一般比RDB快照更大
- AOF開啟後,寫QPS會比RDB的低,因為AOF一般會配置成每s fsync一次日志檔案,當然,每s一次fsync,性能也還是很高的
-
以前AOF發生過bug,就是通過AOF記錄的日志,進行資料恢複的時候,沒有恢複一模一樣的資料出來
類似AOF這種較為複雜的基于指令日志/merge/回放的方式,比基于RDB的每次持久化一份完整的資料快照方式相比更加脆弱一些,易産生bug
不過AOF就是為了避免rewrite過程導緻的bug,是以每次rewrite并不是基于舊的指令日志進行merge的,而是基于當時記憶體中的資料進行指令的重新建構,這樣健壯性會更好
4 選型及最佳實踐
RDB | AOF | |
---|---|---|
啟動優先級 | 低 | 高 |
體積 | ||
恢複速度 | 快 | 慢 |
資料安全性 | 丢資料 | 根據政策決定 |
量級 | 重量級 | 輕量級 |
4.1 RDB最佳政策
- 關閉
- 集中手動管理RDB操作
- 在從節點打開自動執行配置,但是不宜頻繁執行RDB
4.2 AOF最佳政策
- 建議打開,但是如果隻是純作為緩存使用可不開
- AOF重寫集中管理
4.3 抉擇RDB & AOF
- 不要僅使用RDB,因為那樣會導緻你丢失很多資料
- 也不要僅使用AOF,因為那樣有兩個問題
- 你通過AOF做冷備,沒有RDB做冷備,來的恢複速度更快
- RDB每次簡單粗暴生成資料快照,更加健壯,可以避免AOF這種複雜的備份和恢複機制的bug
- 綜合使用AOF和RDB
- 用AOF保證資料不丢失,作為資料恢複的第一選擇
- 用RDB做不同程度的冷備,在AOF檔案都丢失或損壞不可用時,還可使用RDB快速實作資料恢複
4.4 一些最佳實踐
-
小分片
例如設定maxmemory參數設定每個redis隻存儲4個G的空間,這樣各種操作都不會太慢
- 監控(硬碟、記憶體、負載、網絡)
- 足夠的記憶體
參考
- https://redis.io/topics/persistence
- 《Redis設計與實作》