天天看點

突破Java面試(24)-Redis的持久化機制1 持久化概論2 RDB - 全量寫入3 AOF(append only file)- 增量模式4 選型及最佳實踐

全是幹貨的技術号:

本文已收錄在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)。

突破Java面試(24)-Redis的持久化機制1 持久化概論2 RDB - 全量寫入3 AOF(append only file)- 增量模式4 選型及最佳實踐

bgsave

bgsave 也可由

  • 用戶端顯式觸發
  • 配置定時任務觸發
  • 主從架構下由從節點觸發

bgsave指令在執行時,會

fork

一個子程序。子程序送出完成後,會立即給用戶端傳回響應,備份操作在背景異步執行,期間不會影響Redis的正常響應。

對于bgsave來說,當父程序Fork完子程序之後,異步任務會将

目前的記憶體狀态作為一個版本進行複制

在複制過程中産生的變更,不會反映在這次備份當中。

不用指令,而使用配置

在Redis的預設配置中,當滿足下面任一條件,會自動觸發 bgsave 的執行:

配置 seconds changes
save 900 1
300 10
60 10000

​​​​​​bgsave相對于save的優勢是

異步執行

,不影響後續指令執行。但Fork子程序,涉及父程序的記憶體複制,會增加伺服器記憶體開銷。

當記憶體開銷高到使用虛拟記憶體時,bgsave的Fork子程序會阻塞運作

,可能會造成秒級不可用。是以使用bgsave需要保證伺服器空閑記憶體足夠。

突破Java面試(24)-Redis的持久化機制1 持久化概論2 RDB - 全量寫入3 AOF(append only file)- 增量模式4 選型及最佳實踐
指令
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性質

  1. RDB是Redis記憶體到硬碟的快照,用于持久化
  2. save通常會阻塞Redis
  3. bgsave不會阻塞Redis,但會fork新程序
  4. 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記錄的則是每條寫指令的記錄,通過所有寫指令的執行,最後恢複出最終的資料狀态。

突破Java面試(24)-Redis的持久化機制1 持久化概論2 RDB - 全量寫入3 AOF(append only file)- 增量模式4 選型及最佳實踐
  • 其檔案的生成如下:

3.1 寫入流程

AOF的三種政策

突破Java面試(24)-Redis的持久化機制1 持久化概論2 RDB - 全量寫入3 AOF(append only file)- 增量模式4 選型及最佳實踐

always

  • 每次重新整理緩沖區,都會同步觸發同步操作。因為每次的寫操作都會觸發同步,是以該政策會降低Redis的吞吐量,但該 模式會擁有最高的容錯能力。
    突破Java面試(24)-Redis的持久化機制1 持久化概論2 RDB - 全量寫入3 AOF(append only file)- 增量模式4 選型及最佳實踐

every second

  • 每秒異步的觸發同步操作,為Redis的

    預設配置

    突破Java面試(24)-Redis的持久化機制1 持久化概論2 RDB - 全量寫入3 AOF(append only file)- 增量模式4 選型及最佳實踐

no

  • 由作業系統決定何時同步,該方式下Redis無法決定何時落地,是以不可控。
    突破Java面試(24)-Redis的持久化機制1 持久化概論2 RDB - 全量寫入3 AOF(append only file)- 增量模式4 選型及最佳實踐

對比

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

突破Java面試(24)-Redis的持久化機制1 持久化概論2 RDB - 全量寫入3 AOF(append only file)- 增量模式4 選型及最佳實踐

AOF 重寫配置

配置項

  • AOF檔案增長率 / AOF檔案重寫需要的大小
    突破Java面試(24)-Redis的持久化機制1 持久化概論2 RDB - 全量寫入3 AOF(append only file)- 增量模式4 選型及最佳實踐
  • AOF目前尺寸(機關:位元組)
    突破Java面試(24)-Redis的持久化機制1 持久化概論2 RDB - 全量寫入3 AOF(append only file)- 增量模式4 選型及最佳實踐
  • aof_base_size

    AOF 上次啟動和重寫的大小(機關:位元組)

自動觸發配置

aof_current_size > auto-aof-rewrite-min-size
aof_current_size - aof_base_size/aof_base_size > auto-aof-rewrite-percentage           

3.4 AOF 重寫流程

突破Java面試(24)-Redis的持久化機制1 持久化概論2 RDB - 全量寫入3 AOF(append only file)- 增量模式4 選型及最佳實踐

修改配置檔案

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

  1. 不要僅使用RDB,因為那樣會導緻你丢失很多資料
  2. 也不要僅使用AOF,因為那樣有兩個問題
    • 你通過AOF做冷備,沒有RDB做冷備,來的恢複速度更快
    • RDB每次簡單粗暴生成資料快照,更加健壯,可以避免AOF這種複雜的備份和恢複機制的bug
  3. 綜合使用AOF和RDB
    • 用AOF保證資料不丢失,作為資料恢複的第一選擇
    • 用RDB做不同程度的冷備,在AOF檔案都丢失或損壞不可用時,還可使用RDB快速實作資料恢複

4.4 一些最佳實踐

  • 小分片

    例如設定maxmemory參數設定每個redis隻存儲4個G的空間,這樣各種操作都不會太慢

  • 監控(硬碟、記憶體、負載、網絡)
  • 足夠的記憶體

參考