您好,登錄后才能下訂單哦!
本篇內容介紹了“Redis持久化策略是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
Redis(Remote Dictionary Server ),即遠程字典服務,是一個開源的內存高速緩存數據存儲服務。使用 ANSI C 語言編寫,支持網絡、可基于內存亦可持久化的日志型、Key-Value 數據存儲,并提供多種語言的 API
Redis 是內存數據庫,數據都是存儲在內存中,為了避免進程退出導致數據的永久丟失,需要定期將 Redis 中的數據以某種形式(數據或命令)從內存保存到硬盤。當下次 Redis 重啟時,利用持久化文件實現數據恢復。除此之外,為了進行災難備份,可以將持久化文件拷貝到一個遠程位置。Redis 的持久化機制有兩種:
RDB(Redis Data Base) 內存快照
AOF(Append Only File) 增量日志
RDB 將當前數據保存到硬盤,AOF 則是將每次執行的寫命令保存到硬盤(類似于 MySQL 的 Binlog)。AOF 持久化的實時性更好,即當進程意外退出時丟失的數據更少。
RDB ( Redis Data Base) 指的是在指定的時間間隔內將內存中的數據集快照寫入磁盤,RDB 是內存快照(內存數據的二進制序列化形式)的方式持久化,每次都是從 Redis 中生成一個快照進行數據的全量備份。
優點:
存儲緊湊,節省內存空間。
恢復速度非常快。
適合全量備份、全量復制的場景,經常用于災難恢復(對數據的完整性和一致性要求相對較低的場合)。
缺點:
容易丟失數據,容易丟失兩次快照之間 Redis 服務器中變化的數據。
RDB 通過 fork 子進程對內存快照進行全量備份,是一個重量級操作,頻繁執行成本高。
在默認情況下,Redis 將數據庫快照保存在名字為 dump.rdb 的二進制文件中。RDB 文件結構由五個部分組成:
(1)長度為5字節的 REDIS
常量字符串。
(2)4字節的 db_version,標識 RDB 文件版本。
(3)databases:不定長度,包含零個或多個數據庫,以及各數據庫中的鍵值對數據。
(4)1字節的 EOF 常量,表示文件正文內容結束。
(5)check_sum: 8字節長的無符號整數,保存校驗和。
數據結構舉例,以下是數據庫[0]和數據庫[3]有數據的情況:
手動指令觸發
手動觸發 RDB 持久化的方式可以使用 save
命令和 bgsave
命令,這兩個命令的區別如下:
save
:執行 save
指令,阻塞 Redis 的其他操作,會導致 Redis 無法響應客戶端請求,不建議使用。
bgsave
:執行 bgsave
指令,Redis 后臺創建子進程,異步進行快照的保存操作,此時 Redis 仍然能響應客戶端的請求。
自動間隔性保存
在默認情況下,Redis 將數據庫快照保存在名字為 dump.rdb的二進制文件中。可以對 Redis 進行設置,讓它在“ N 秒內數據集至少有 M 個改動”這一條件被滿足時,自動保存一次數據集。
比如說, 以下設置會讓 Redis 在滿足“ 60 秒內有至少有 10 個鍵被改動”這一條件時,自動保存一次數據集:save 60 10
。
Redis 的默認配置如下,三個設置滿足其一即可觸發自動保存:
save 60 10000
save 300 10
save 900 1
自動保存配置的數據結構
記錄了服務器觸發自動 BGSAVE
條件的saveparams
屬性。
lastsave
屬性:記錄服務器最后一次執行 SAVE
或者 BGSAVE
的時間。
dirty
屬性:以及自最后一次保存 RDB 文件以來,服務器進行了多少次寫入。
RDB 持久化方案進行備份時,Redis 會單獨 fork 一個子進程來進行持久化,會將數據寫入一個臨時文件中,持久化完成后替換舊的 RDB 文件。在整個持久化過程中,主進程(為客戶端提供服務的進程)不參與 IO 操作,這樣能確保 Redis 服務的高性能,RDB 持久化機制適合對數據完整性要求不高但追求高效恢復的使用場景。下面展示 RDB 持久化流程:
關鍵執行步驟如下
Redis 父進程首先判斷:當前是否在執行 save,或 bgsave/bgrewriteaof 的子進程,如果在執行則 bgsave 命令直接返回。bgsave/bgrewriteaof 的子進程不能同時執行,主要是基于性能方面的考慮:兩個并發的子進程同時執行大量的磁盤寫操作,可能引起嚴重的性能問題。
父進程執行 fork 操作創建子進程,這個過程中父進程是阻塞的,Redis 不能執行來自客戶端的任何命令。父進程 fork 后,bgsave 命令返回”Background saving started”信息并不再阻塞父進程,并可以響應其他命令。
子進程進程對內存數據生成快照文件。
父進程在此期間接收的新的寫操作,使用 COW 機制寫入。
子進程完成快照寫入,替換舊 RDB 文件,隨后子進程退出。
在生成 RDB 文件的步驟中,在同步到磁盤和持續寫入這個過程是如何處理數據不一致的情況呢?生成快照 RDB 文件時是否會對業務產生影響?
上面說到了 RDB 持久化過程中,主進程會 fork 一個子進程來負責 RDB 的備份,這里簡單介紹一下 fork:
Linux 操作系統中的程序,fork 會產生一個和父進程完全相同的子進程。子進程與父進程所有的數據均一致,但是子進程是一個全新的進程,與原進程是父子進程關系。
出于效率考慮,Linux 操作系統中使用 COW(Copy On Write)寫時復制機制,fork 子進程一般情況下與父進程共同使用一段物理內存,只有在進程空間中的內存發生修改時,內存空間才會復制一份出來。
在 Redis 中,RDB 持久化就是充分的利用了這項技術,Redis 在持久化時調用 glibc 函數 fork 一個子進程,全權負責持久化工作,這樣父進程仍然能繼續給客戶端提供服務。fork 的子進程初始時與父進程(Redis 的主進程)共享同一塊內存;當持久化過程中,客戶端的請求對內存中的數據進行修改,此時就會通過 COW (Copy On Write) 機制對數據段頁面進行分離,也就是復制一塊內存出來給主進程去修改。
通過 fork 創建的子進程能夠獲得和父進程完全相同的內存空間,父進程對內存的修改對于子進程是不可見的,兩者不會相互影響;
通過 fork 創建子進程時不會立刻觸發大量內存的拷貝,采用的是寫時拷貝 COW (Copy On Write)。內核只為新生成的子進程創建虛擬空間結構,它們來復制于父進程的虛擬究竟結構,但是不為這些段分配物理內存,它們共享父進程的物理空間,當父子進程中有更改相應段的行為發生時,再為子進程相應的段分配物理空間;
AOF (Append Only File) 是把所有對內存進行修改的指令(寫操作)以獨立日志文件的方式進行記錄,重啟時通過執行 AOF 文件中的 Redis 命令來恢復數據。類似MySql bin-log 原理。AOF 能夠解決數據持久化實時性問題,是現在 Redis 持久化機制中主流的持久化方案。
優點:
數據的備份更加完整,丟失數據的概率更低,適合對數據完整性要求高的場景
日志文件可讀,AOF 可操作性更強,可通過操作日志文件進行修復
缺點:
AOF 日志記錄在長期運行中逐漸龐大,恢復起來非常耗時,需要定期對 AOF 日志進行瘦身處理
恢復備份速度比較慢
同步寫操作頻繁會帶來性能壓力
被寫入 AOF 文件的所有命令都是以 RESP 格式保存的,是純文本格式保存在 AOF 文件中。
Redis 客戶端和服務端之間使用一種名為
RESP(REdis Serialization Protocol)
的二進制安全文本協議進行通信。
下面以一個簡單的 SET 命令進行舉例:
redis> SET mykey "hello"
//客戶端命令OK
客戶端封裝為以下格式(每行用 \r\n
分隔)
*3$3SET$5mykey$5hello
AOF 文件中記錄的文本內容如下
*2\r\n$6\r\nSELECT\r\n$1\r\n0\r\n
//多出一個SELECT 0 命令,用于指定數據庫,為系統自動添加
*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$5\r\nhello\r\n
AOF 持久化方案進行備份時,客戶端所有請求的寫命令都會被追加到 AOF 緩沖區中,緩沖區中的數據會根據 Redis 配置文件中配置的同步策略來同步到磁盤上的 AOF 文件中,追加保存每次寫的操作到文件末尾。同時當 AOF 的文件達到重寫策略配置的閾值時,Redis 會對 AOF 日志文件進行重寫,給 AOF 日志文件瘦身。Redis 服務重啟的時候,通過加載 AOF 日志文件來恢復數據。
AOF 的執行流程包括:
命令追加(append)
Redis 先將寫命令追加到緩沖區 aof_buf,而不是直接寫入文件,主要是為了避免每次有寫命令都直接寫入硬盤,導致硬盤 IO 成為 Redis 負載的瓶頸。
struct redisServer {
//其他域... sds aof_buf; // sds類似于Java中的String //其他域...}
文件寫入(write)和文件同步(sync)
根據不同的同步策略將 aof_buf 中的內容同步到硬盤;
Linux 操作系統中為了提升性能,使用了頁緩存(page cache)。當我們將 aof_buf 的內容寫到磁盤上時,此時數據并沒有真正的落盤,而是在 page cache 中,為了將 page cache 中的數據真正落盤,需要執行 fsync / fdatasync 命令來強制刷盤。這邊的文件同步做的就是刷盤操作,或者叫文件刷盤可能更容易理解一些。
AOF 緩存區的同步文件策略由參數 appendfsync 控制,有三種同步策略,各個值的含義如下:
always
:命令寫入 aof_buf 后立即調用系統 write 操作和系統 fsync 操作同步到 AOF 文件,fsync 完成后線程返回。這種情況下,每次有寫命令都要同步到 AOF 文件,硬盤 IO 成為性能瓶頸,Redis 只能支持大約幾百TPS寫入,嚴重降低了 Redis 的性能;即便是使用固態硬盤(SSD),每秒大約也只能處理幾萬個命令,而且會大大降低 SSD 的壽命。可靠性較高,數據基本不丟失。
no
:命令寫入 aof_buf 后調用系統 write 操作,不對 AOF 文件做 fsync 同步;同步由操作系統負責,通常同步周期為30秒。這種情況下,文件同步的時間不可控,且緩沖區中堆積的數據會很多,數據安全性無法保證。
everysec
:命令寫入 aof_buf 后調用系統 write 操作,write 完成后線程返回;fsync 同步文件操作由專門的線程每秒調用一次。everysec 是前述兩種策略的折中,是性能和數據安全性的平衡,因此是 Redis 的默認配置,也是我們推薦的配置。
文件重寫(rewrite)
定期重寫 AOF 文件,達到壓縮的目的。
AOF 重寫是 AOF 持久化的一個機制,用來壓縮 AOF 文件,通過 fork 一個子進程,重新寫一個新的 AOF 文件,該次重寫不是讀取舊的 AOF 文件進行復制,而是讀取內存中的Redis數據庫,重寫一份 AOF 文件,有點類似于 RDB 的快照方式。
文件重寫之所以能夠壓縮 AOF 文件,原因在于:
過期的數據不再寫入文件
無效的命令不再寫入文件:如有些數據被重復設值(set mykey v1, set mykey v2)、有些數據被刪除了(sadd myset v1, del myset)等等
多條命令可以合并為一個:如 sadd myset v1, sadd myset v2, sadd myset v3 可以合并為 sadd myset v1 v2 v3。不過為了防止單條命令過大造成客戶端緩沖區溢出,對于 list、set、hash、zset類型的 key,并不一定只使用一條命令;而是以某個常量為界將命令拆分為多條。這個常量在 redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD 中定義,不可更改,2.9版本中值是64。
前面提到 AOF 的缺點時,說過 AOF 屬于日志追加的形式來存儲 Redis 的寫指令,這會導致大量冗余的指令存儲,從而使得 AOF 日志文件非常龐大,比如同一個 key 被寫了 10000 次,最后卻被刪除了,這種情況不僅占內存,也會導致恢復的時候非常緩慢,因此 Redis 提供重寫機制來解決這個問題。Redis 的 AOF 持久化機制執行重寫后,保存的只是恢復數據的最小指令集,我們如果想手動觸發可以使用如下指令:
bgrewriteaof
文件重寫時機
相關參數:
aof_current_size:表示當前 AOF 文件空間
aof_base_size:表示上一次重寫后 AOF 文件空間
auto-aof-rewrite-min-size: 表示運行 AOF 重寫時文件的最小體積,默認為64MB
auto-aof-rewrite-percentage: 表示當前 AOF 重寫時文件空間(aof_current_size)超過上一次重寫后 AOF 文件空間(aof_base_size)的比值多少后會重寫。
同時滿足下面兩個條件,則觸發 AOF 重寫機制:
aof_current_size 大于 auto-aof-rewrite-min-size
當前 AOF 相比上一次 AOF 的增長率:(aof_current_size - aof_base_size)/aof_base_size 大于或等于 auto-aof-rewrite-percentage
AOF 重寫流程如下:
bgrewriteaof 觸發重寫,判斷是否存在 bgsave 或者 bgrewriteaof 正在執行,存在則等待其執行結束再執行
主進程 fork 子進程,防止主進程阻塞無法提供服務,類似 RDB
子進程遍歷 Redis 內存快照中數據寫入臨時 AOF 文件,同時會將新的寫指令寫入 aof_buf 和 aof_rewrite_buf 兩個重寫緩沖區,前者是為了寫回舊的 AOF 文件,后者是為了后續刷新到臨時 AOF 文件中,防止快照內存遍歷時新的寫入操作丟失
子進程結束臨時 AOF 文件寫入后,通知主進程
主進程會將上面 3 中的 aof_rewirte_buf 緩沖區中的數據寫入到子進程生成的臨時 AOF 文件中
主進程使用臨時 AOF 文件替換舊 AOF 文件,完成整個重寫過程。
在實際中,為了避免在執行命令時造成客戶端輸入緩沖區溢出,重寫程序會檢查集合元素數量是否超過 REDIS_AOF_REWRITE_ITEMS_PER_CMD 常量的值,如果超過了,則會使用多個命令來記錄,而不單單使用一條命令。
Redis 2.9版本中該常量為64,如果一個命令的集合鍵包含超過了64個元素,重寫程序會拆成多個命令。
AOF重寫是一個有歧義的名字,該功能是通過直接讀取數據庫的鍵值對實現的,程序無需對現有AOF文件進行任何讀入、分析或者寫入操作。
Redis 為什么考慮使用 AOF 而不是 WAL 呢?
很多數據庫都是采用的 Write Ahead Log(WAL)寫前日志,其特點就是先把修改的數據記錄到日志中,再進行寫數據的提交,可以方便通過日志進行數據恢復。
但是 Redis 采用的卻是 AOF(Append Only File)寫后日志,特點就是先執行寫命令,把數據寫入內存中,再記錄日志。
如果先讓系統執行命令,只有命令能執行成功,才會被記錄到日志中。因此,Redis 使用寫后日志這種形式,可以避免出現記錄錯誤命令的情況。
另外還有一個原因就是:AOF 是在命令執行后才記錄日志,所以不會阻塞當前的寫操作。
在版本號大于等于2.4的 Redis 中,BGSAVE 執行的過程中,不可以執行 BGREWRITEAOF。反過來說,在 BGREWRITEAOF 執行的過程中,也不可以執行 BGSAVE。這可以防止兩個 Redis 后臺進程同時對磁盤進行大量的 I/O 操作。
如果 BGSAVE 正在執行,并且用戶顯示地調用 BGREWRITEAOF 命令,那么服務器將向用戶回復一個 OK 狀態,并告知用戶,BGREWRITEAOF 已經被預定執行:一旦 BGSAVE 執行完畢 BGREWRITEAOF 就會正式開始。
當 Redis 啟動時,如果 RDB 持久化和 AOF 持久化都被打開了,那么程序會優先使用 AOF 文件來恢復數據集,因為 AOF 文件所保存的數據通常是最完整的。
Redis4.0 后大部分的使用場景都不會單獨使用 RDB 或者 AOF 來做持久化機制,而是兼顧二者的優勢混合使用。其原因是 RDB 雖然快,但是會丟失比較多的數據,不能保證數據完整性;AOF 雖然能盡可能保證數據完整性,但是性能確實是一個詬病,比如重放恢復數據。
Redis從4.0版本開始引入 RDB-AOF 混合持久化模式,這種模式是基于 AOF 持久化模式構建而來的,混合持久化通過 aof-use-rdb-preamble yes
開啟。
那么 Redis 服務器在執行 AOF 重寫操作時,就會像執行 BGSAVE 命令那樣,根據數據庫當前的狀態生成出相應的 RDB 數據,并將這些數據寫入新建的 AOF 文件中,至于那些在 AOF 重寫開始之后執行的 Redis 命令,則會繼續以協議文本的方式追加到新 AOF 文件的末尾,即已有的 RDB 數據的后面。
換句話說,在開啟了 RDB-AOF 混合持久化功能之后,服務器生成的 AOF 文件將由兩個部分組成,其中位于 AOF 文件開頭的是 RDB 格式的數據,而跟在 RDB 數據后面的則是 AOF 格式的數據。
當一個支持 RDB-AOF 混合持久化模式的 Redis 服務器啟動并載入 AOF 文件時,它會檢查 AOF 文件的開頭是否包含了 RDB 格式的內容。
如果包含,那么服務器就會先載入開頭的 RDB 數據,然后再載入之后的 AOF 數據。
如果 AOF 文件只包含 AOF 數據,那么服務器將直接載入 AOF 數據。
其日志文件結構如下:
“Redis持久化策略是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。