小伙用 12 张图讲明白了 Redis 持久化!
00 前言
很多小伙伴都用 Redis 做緩存,那如果 Redis 服務器宕機,內存中數據全部丟失,應該如何做數據恢復呢?有人說很簡單呀,直接從 MySQL 數據庫再讀回來就得了。
這種方式存在兩個問題:一是頻繁訪問?MySQL 數據庫,有一定的風險;二是慢,從界面上來看,從 MySQL 讀就不如從 Redis 快。
遠哥遠哥,那咋辦呀?教教我吧。
我用中指抵著小胖的下吧,說到:傻瓜,我們可以做持久化呀。Redis 的持久化分兩種,一種是 AOF,另一種是 RDB。來,坐哥哥腿上,我給你好好說道說道。
老規矩,先上張腦圖:
0.1 什么是持久化?
持久化(Persistence),即把數據(如內存中的對象)保存到可永久保存的存儲設備中(如磁盤)。持久化的主要應用是將內存中的對象存儲在數據庫中,或者存儲在磁盤文件中、XML 數據文件中等等。持久化是將程序數據在持久狀態和瞬時狀態間轉換的機制
01 怎么理解 Redis 的單線程?
必須聲明一點:Redis 的單線程,是指?Redis 的網絡 IO 和鍵值對讀寫是由一個線程(主線程)完成的,這也是 Redis 對外提供鍵值存儲服務的主要流程。但 Redis 的其他功能,比如持久化、異步刪除、集群數據同步等,其實是由額外的線程執行的。
1.0 Redis 快的原因?
基于內存
數據都存儲在內存里,減少了一些不必要的 I/O 操作,操作速率很快。
高效的數據結構
底層多種數據結構支持不同的數據類型,支持 Redis 存儲不同的數據;
不同數據結構的設計,使得數據存儲時間復雜度降到最低。
合理的線程模型
I/O 多路復用模型同時監聽多個客戶端連接;
單線程在執行過程中不需要進行上下文切換,減少了耗時。
02 AOF 持久化
AOF(Append Only File) 持久化是通過保存 Redis 服務器所執行的寫命令來記錄數據庫狀態,也就是每當 Redis 執行一個改變數據集的命令時(比如 SET), 這個命令就會被追加到 AOF 文件的末尾。
修改 redis.conf 配置文件,默認是 appendonly no(關閉狀態),將 no 改為 yes 即可開啟 AOF 持久化:
appendonly?yes在客戶端輸入如下命令也可,但是 Redis 服務器重啟后會失效。
192.168.17.101:6379>?config?set?appendonly?yes OKAOF 持久化功能的實現可以分為命令追加(append)、文件寫回磁盤兩個步驟。
2.0 命令追加
AOF 持久化功能開啟時,Redis 在執行完一個寫命令之后,會將被執行的寫命令追加到服務器狀態的?aof_buf 緩沖區的末尾,此時緩沖區的記錄還沒有寫入到 appendonly.aof 文件中。
2.0.1 AOF 的格式
AOF 保存的是 Redis 的寫命令,比如:執行命令?set testkey testvalue,它存儲的內容如下圖所示:
AOF 格式其中,“*3” 表示當前命令有三個部分,每部分都是由?$+ 數字開頭,后面緊跟著具體的命令、鍵或值。這里,數字表示這部分中的命令、鍵或值一共有多少字節。例如,?$3 set?表示這部分有 3 個字節,也就是?set?命令。
2.0.2 寫后日志有啥優缺點?
AOF 記錄日志的方式被稱為寫后日志,也就是先執行命令再記錄,而 MySQL 中的 redo log、binlog 等都是寫前日志。它的寫入流程是下圖這樣的:
寫 AOF 流程寫后有什么優點?
記錄 AOF 時不會對命令進行語法檢查 ,寫后就只記錄了執行成功的命令。(避免保存的錯誤的命令,恢復的時候就完犢子了)
執行完之后再記錄,不會阻塞當前的寫操作
寫后有什么缺陷?
如果執行完一個命令還沒來得及寫日志就宕機了會造成響應數據丟失。
AOF 的寫入由主線程處理,如果寫入時出現較長耗時,那就會影響主線程處理后續的請求。
你發現沒有?寫后的兩個缺陷都是 AOF 的寫入磁盤時相發生的,我們來看看它是怎么寫入的呢?
2.1 AOF 寫入磁盤
AOF 提供了三個選擇,也就是 AOF 配置項?appendfsync?的三個可選值。
Always,同步寫回:每個寫命令執行完,立馬同步地將日志寫回磁盤;
Everysec(默認),每秒寫回:每個寫命令執行完,只是先把日志寫到 AOF 文件的內存緩沖區,每隔一秒把緩沖區中的內容寫入磁盤;
No,操作系統控制的寫回:每個寫命令執行完,只是先把日志寫到 AOF 文件的內存緩沖區,由操作系統決定何時將緩沖區內容寫回磁盤。
2.1.0 三種策略的優缺點
針對避免主線程阻塞和減少數據丟失問題,這三種寫回策略都無法做到兩全其美。主要原因是:
Always(同步寫回) 基本不丟數據,但是它在每一個寫命令后都有一個慢速的落盤操作,影響主線程性能;
No(操作系統控制的寫回)在寫完緩沖區后,繼續執行后續的命令,但是落盤的時機已經不在 Redis 手中了,只要 AOF 記錄沒有寫回磁盤,一旦宕機對應的數據就丟失了;
Everysec(每秒寫回)采用一秒寫回一次的頻率,避免了 Always 的性能開銷,雖然減少了對系統性能的影響,但是如果發生宕機,上一秒內未落盤的命令操作仍然會丟失。
總結一下就是:想高性能,選擇 No 策略;想高可靠性,選擇 Always 策略;允許數據有一點丟失,又希望性能別受太大影響,選擇 Everysec 策略。
2.2 AOF 恢復數據
不說了,看圖:
AOF 恢復數據2.3 AOF 重寫
我不知道你發現沒有?AOF 文件是不斷地將寫命令追加到文件的末尾來記錄數據庫狀態的。寫命令不斷增加,AOF 體積也越來越大。
有些命令是執行多次更新同一條數據,但其實它是可以合并成同一條命令的。比如:LPUSH 對列表數據做了 6 次更改,但 AOF 只需要記錄最后一次更改。因為日志恢復時,只需要執行最后一次更改的命令即可。
為了處理這種情況,Redis 提供了 AOF 的重寫機制。它的多變一功能,把 6 條寫命令合并成一條。如下所示:
LPUSH如果你的某些鍵有成百上千次的修改,重寫機制節約的空間就很可觀了。
2.3.1 觸發重寫
有兩種觸發的方法,一個是調用命令 BGREWRITEAOF;一個是修改配置文件參數。
#?方式一 192.168.17.101:6379>?BGREWRITEAOF Background?append?only?file?rewriting?started#?方式二 auto-aof-rewrite-percentage?100?#當前AOF文件大小和上一次重寫時AOF文件大小的比值 auto-aof-rewrite-min-size?64mb??#文件的最小體積2.3.2 重寫步驟
創建子進程進行 AOF 重寫
將客戶端的寫命令追加到 AOF 重寫緩沖區
子進程完成 AOF 重寫工作后,會向父進程發送一個信號
父進程接收到信號后,將 AOF 重寫緩沖區的所有內容寫入到新 AOF 文件中
對新的 AOF 文件進行改名,覆蓋現有的 AOF 文件
2.4 相關配置
#?是否開啟AOF功能 appendonly?no#?AOF文件件名稱 appendfilename?"appendonly.aof"#?寫入AOF文件的三種方式 appendfsync?always appendfsync?everysec appendfsync?no#?重寫AOF時,是否繼續寫AOF文件 no-appendfsync-on-rewrite?no#?自動重寫AOF文件的條件 auto-aof-rewrite-percentage?100?#百分比 auto-aof-rewrite-min-size?64mb?#大小#?是否忽略最后一條可能存在問題的指令 aof-load-truncated?yes2.5 優缺點
優點
AOF 文件可讀性高,分析容易
AOF 文件過大時,自動進行重寫
追加形式,寫入時不需要再次讀取文件,直接加到末尾
缺點
相同數據量下,AOF 一般比 RDB 大
AOF 恢復時需要重放命令,恢復速度慢
根據 fsync 策略,AOF 的速度可能慢于 RDB
03 RDB 持久化
RDB 持久化是指在客戶端輸入?save、bgsave?或者達到配置文件自動保存快照條件時,將 Redis 在內存中的數據生成快照保存在名字為?dump.rdb(文件名可修改)的二進制文件中。
3.1 save 命令
save 命令會阻塞 Redis 服務器進程,直到 RDB 文件創建完畢為止,在 Redis 服務器阻塞期間,服務器不能處理任何命令請求。在客戶端輸入 save
127.0.0.1:6379>?save OK快照生成完畢,會彈出?DB saved ondisk?的提示。
1349:M?25?Apr?13:16:48.935?*?DB?saved?on?disk3.2 bgsave 命令
bgsave 執行時,主線程會創建一個子進程,專門用于寫入 RDB 文件,避免了主線程的阻塞,這也是 Redis RDB 文件生成的默認配置。
127.0.0.1:6379>?bgsave Background?saving?startedPS:bgsave 命令執行期間 SAVE 命令會被拒絕;不能同時執行兩個 BGSAVE 命令;不能同時執行 BGREWRITEAOF 和 BGSAVE 命令。
3.3 bgsave 時寫數據
bgsave 執行時,Redis 主線程能正常讀寫數據。讀操作時,主線程和 bgsave 子線程互不影響;寫操作時,Redis 會利用寫時復制技術(Copy-On-Write, COW),生成被修改數據的副本。然后 bgsave 子線程把副本數據寫入 RDB。
比如,bgsave 期間,主線程修改鍵值對 C,過程如下:
寫時復制技術但是在這過程中發生宕機了咋辦?比如,T0 時刻做了一次快照,T0+t 時刻又做了一次。但是 t 時間內主線程修改完數據 5 和 9,然后 Redis 宕機了,RDB 沒記錄到修改后的數據。
Redis 重啟恢復數據,就會出現數據 5 和 9 丟失的情況,沒辦法恢復。
丟失數據這該咋辦?我們需要記住那些數據被修改了。
3.4 混合持久化
如下圖所示,記錄 t 時刻被修改的數據就需要占用額外的空間,而 Redis 是內存數據庫,空間非常寶貴。所以,直接記錄到內存這種方式不可取。
增量快照內存開銷比較小的方法是把 t 時間的增量寫操作記錄到 AOF 日志中,這樣既保留了 RDB 的快速恢復,也沒占用額外的空間。
如圖,T1 和 T2 時刻的修改,用 AOF 日志記錄,等第二次做全量快照時,清空 AOF 日志,因為此時的修改都記錄到快照中了,恢復不用 AOF 日志了。
AOF 記錄增量修改慶幸的是 Redis 4.0 就開始提供了這種?RDB + AOF 的持久化方式,開啟的配置項是?aof-use-rdb-preamble yes,它需要配合 AOF 的重寫機制實現。
#?開啟混合持久化 redis>?config?set?aof-use-rdb-preamble?yes OK #?AOF?重寫 redis>?BGREWRITEAOF Background?append?only?file?rewriting?started在沒有第二次做全量快照之前,它的格式是這樣的:前半部分是 RDB 格式,后半部分是 AOF 增量日志。如果這個時候宕機,直接拿 appendonly.aof 恢復數據。
appendonly.aof 格式3.5 RDB 優缺點
優點
二進制數據,恢復時比 AOF 快
RDB 的 bgsave 方式主線程不阻塞
缺點
Redis 意外宕機 時,會丟失部分數據(混合持久化可解決)
當數據量比較大時,fork 的過程是非常耗時的,fork 子進程時是會阻塞的,在這期間 Redis 是不能響應客戶端的請求的。
04 如何選擇?
數據不能丟失時,選擇內存快照和 AOF 混合使用;
如果允許分鐘級別的數據丟失,可以只使用 RDB;
如果只用 AOF,優先使用 everysec 的配置選項,因為它在可靠性和性能之間取了一個平衡。
05 數據恢復流程
數據恢復流程07 總結
本文主要講解 Redis AOF 、RDB 持久化的原理和兩者的優缺點,對比兩者后,我還給你總結了 Redis 混合持久化的流程。最后給了你一些選擇持久化方案的建議,希望看完你能有所收獲。
全文將近字,張圖,希望能幫到你。好啦,以上就是狗哥關于 Redis 持久化的總結。感謝各技術社區大佬們的付出,尤其是極客時間,真的牛逼。如果說我看得更遠,那是因為我站在你們的肩膀上。
巨人的肩膀
《Redis 設計與實現》
juejin.cn/post/6844903648976175118
blog.csdn.net/weixin_33810006/article/details/90394921
blog.csdn.net/wsdc0521/article/details/106765809
完
往期推薦
7000 字,四年多 Java 的 BAT 面經分享!
代碼review,瑞出事來了!
synchronized 底層了解一下...
有道無術,術可成;有術無道,止于術
歡迎大家關注Java之道公眾號
好文章,我在看??
總結
以上是生活随笔為你收集整理的小伙用 12 张图讲明白了 Redis 持久化!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 求解九宫格的Java_使用全排列方法解九
- 下一篇: python importlib_学习p