您好,登錄后才能下訂單哦!
這篇文章主要介紹“Redis的高可用特性持久化怎么實現”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Redis的高可用特性持久化怎么實現”文章能幫助大家解決問題。
Redis 高可用概述
在介紹 Redis 高可用之前,先說明一下在 Redis 的語境中高可用的含義。在 Web 服務器中,高可用是指服務器可以正常訪問的時間,衡量的標準是在多長時間內可以提供正常服務(99.9%、99.99%、99.999% 等等)。
但是在 Redis 語境中,高可用的含義似乎要寬泛一些,除了保證提供正常服務(如主從分離、快速容災技術),還需要考慮數據容量的擴展、數據安全不會丟失等。
在 Redis 中,實現高可用的技術主要包括持久化、復制、哨兵和集群,下面分別說明它們的作用,以及解決了什么樣的問題:
持久化:持久化是最簡單的高可用方法(有時甚至不被歸為高可用的手段),主要作用是數據備份,即將數據存儲在硬盤,保證數據不會因進程退出而丟失。
復制:復制是高可用 Redis 的基礎,哨兵和集群都是在復制基礎上實現高可用的。復制主要實現了數據的多機備份,以及對于讀操作的負載均衡和簡單的故障恢復。
缺陷:故障恢復無法自動化,寫操作無法負載均衡,存儲能力受到單機的限制。
哨兵:在復制的基礎上,哨兵實現了自動化的故障恢復。
缺陷:寫操作無法負載均衡;存儲能力受到單機的限制。
集群:通過集群,Redis 解決了寫操作無法負載均衡,以及存儲能力受到單機限制的問題,實現了較為完善的高可用方案。
Redis 持久化概述
持久化的功能:Redis 是內存數據庫,數據都是存儲在內存中。
為了避免進程退出導致數據的***丟失,需要定期將 Redis 中的數據以某種形式(數據或命令)從內存保存到硬盤;當下次 Redis 重啟時,利用持久化文件實現數據恢復。
除此之外,為了進行災難備份,可以將持久化文件拷貝到一個遠程位置。
Redis 持久化分為 RDB 持久化和 AOF 持久化:
前者將當前數據保存到硬盤
后者則是將每次執行的寫命令保存到硬盤(類似于 MySQL 的 binlog)
由于 AOF 持久化的實時性更好,即當進程意外退出時丟失的數據更少,因此 AOF 是目前主流的持久化方式,不過 RDB 持久化仍然有其用武之地。
下面依次介紹 RDB 持久化和 AOF 持久化;由于 Redis 各個版本之間存在差異,如無特殊說明,以 Redis 3.0 為準。
RDB 持久化
RDB 持久化是將當前進程中的數據生成快照保存到硬盤(因此也稱作快照持久化),保存的文件后綴是 RDB;當 Redis 重新啟動時,可以讀取快照文件恢復數據。
觸發條件
RDB 持久化的觸發分為手動觸發和自動觸發兩種:
手動觸發
自動觸發
手動觸發:save 命令和 bgsave 命令都可以生成 RDB 文件。
save 命令會阻塞 Redis 服務器進程,直到 RDB 文件創建完畢為止,在 Redis 服務器阻塞期間,服務器不能處理任何命令請求。
而 bgsave 命令會創建一個子進程,由子進程來負責創建 RDB 文件,父進程(即 Redis 主進程)則繼續處理請求。
此時服務器執行日志如下:
bgsave 命令執行過程中,只有 fork 子進程時會阻塞服務器,而對于 save 命令,整個過程都會阻塞服務器。
因此 save 已基本被廢棄,線上環境要杜絕 save 的使用;后文中也將只介紹 bgsave 命令。
此外,在自動觸發 RDB 持久化時,Redis 也會選擇 bgsave 而不是 save 來進行持久化;下面介紹自動觸發 RDB 持久化的條件。
自動觸發:最常見的情況是在配置文件中通過 save m n,指定當 m 秒內發生 n 次變化時,會觸發 bgsave。
例如,查看 Redis 的默認配置文件(Linux 下為 Redis 根目錄下的 redis.conf),可以看到如下配置信息:
其中 save 900 1 的含義是:當時間到 900 秒時,如果 Redis 數據發生了至少 1 次變化,則執行 bgsave。
save 300 10 和 save 60 10000 同理,當三個 save 條件滿足任意一個時,都會引起 bgsave 的調用。
save m n 的實現原理:Redis 的 save m n,是通過 serverCron 函數、dirty 計數器和 lastsave 時間戳來實現的。
serverCron 是 Redis 服務器的周期性操作函數,默認每隔 100ms 執行一次;該函數對服務器的狀態進行維護,其中一項工作就是檢查 save m n 配置的條件是否滿足,如果滿足就執行 bgsave。
dirty 計數器是 Redis 服務器維持的一個狀態,記錄了上一次執行 bgsave/save 命令后,服務器狀態進行了多少次修改(包括增刪改);而當 save/bgsave 執行完成后,會將 dirty 重新置為 0。
例如,如果 Redis 執行了 set mykey helloworld,則 dirty 值會 +1;如果執行了 sadd myset v1 v2 v3,則 dirty 值會 +3;注意 dirty 記錄的是服務器進行了多少次修改,而不是客戶端執行了多少修改數據的命令。
lastsave 時間戳也是 Redis 服務器維持的一個狀態,記錄的是上一次成功執行 save/bgsave 的時間。
save m n 的原理如下:每隔 100ms,執行 serverCron 函數;在 serverCron 函數中,遍歷 save m n 配置的保存條件,只要有一個條件滿足,就進行 bgsave。
對于每一個 save m n 條件,只有下面兩條同時滿足時才算滿足:
當前時間-lastsave > m
dirty >= n
save m n 執行日志:下圖是 save m n 觸發 bgsave 執行時,服務器打印日志的情況。
除了 save m n 以外,還有一些其他情況會觸發 bgsave:
在主從復制場景下,如果從節點執行全量復制操作,則主節點會執行 bgsave 命令,并將 RDB 文件發送給從節點。
執行 shutdown 命令時,自動執行 RDB 持久化,如下圖所示:
執行流程
前面介紹了觸發 bgsave 的條件,下面將說明 bgsave 命令的執行流程,如下圖所示:
圖片中的 5 個步驟所進行的操作如下:
Redis 父進程首先判斷:當前是否在執行 save 或 bgsave/bgrewriteaof(后面會詳細介紹該命令)的子進程,如果在執行則 bgsave 命令直接返回。
bgsave/bgrewriteaof 的子進程不能同時執行,主要是基于性能方面的考慮:兩個并發的子進程同時執行大量的磁盤寫操作,可能引起嚴重的性能問題。
父進程執行 fork 操作創建子進程,這個過程中父進程是阻塞的,Redis 不能執行來自客戶端的任何命令。
父進程 fork 后,bgsave 命令返回”Background saving started”信息并不再阻塞父進程,并可以響應其他命令。
子進程創建 RDB 文件,根據父進程內存快照生成臨時快照文件,完成后對原有文件進行原子替換。
子進程發送信號給父進程表示完成,父進程更新統計信息。
RDB 文件
RDB 文件是經過壓縮的二進制文件,下面介紹關于 RDB 文件的一些細節。
存儲路徑
RDB 文件的存儲路徑既可以在啟動前配置,也可以通過命令動態設定。
配置:dir 配置指定目錄,dbfilename 指定文件名。默認是 Redis 根目錄下的 dump.rdb 文件。
動態設定:Redis 啟動后也可以動態修改 RDB 存儲路徑,在磁盤損害或空間不足時非常有用;執行命令為 config set dir {newdir}和 config set dbfilename {newFileName}。
如下所示(Windows 環境):
RDB 文件格式
RDB 文件格式如下圖所示:
其中各個字段的含義說明如下:
REDIS:常量,保存著“REDIS”5 個字符。
db_version:RDB 文件的版本號,注意不是 Redis 的版本號。
SELECTDB 0 pairs:表示一個完整的數據庫(0 號數據庫),同理 SELECTDB 3 pairs 表示完整的 3 號數據庫。
只有當數據庫中有鍵值對時,RDB 文件中才會有該數據庫的信息(上圖所示的 Redis 中只有 0 號和 3 號數據庫有鍵值對);如果 Redis 中所有的數據庫都沒有鍵值對,則這一部分直接省略。
其中:SELECTDB 是一個常量,代表后面跟著的是數據庫號碼;0 和 3 是數據庫號碼;pairs 則存儲了具體的鍵值對信息,包括 key、value 值,及其數據類型、內部編碼、過期時間、壓縮信息等等。
EOF:常量,標志 RDB 文件正文內容結束。
check_sum:前面所有內容的校驗和;Redis 在載入 RBD 文件時,會計算前面的校驗和并與 check_sum 值比較,判斷文件是否損壞。
壓縮
Redis 默認采用 LZF 算法對 RDB 文件進行壓縮。雖然壓縮耗時,但是可以大大減小 RDB 文件的體積,因此壓縮默認開啟;可以通過命令關閉:
需要注意的是,RDB 文件的壓縮并不是針對整個文件進行的,而是對數據庫中的字符串進行的,且只有在字符串達到一定長度(20 字節)時才會進行。
啟動時加載
RDB 文件的載入工作是在服務器啟動時自動執行的,并沒有專門的命令。但是由于 AOF 的優先級更高,因此當 AOF 開啟時,Redis 會優先載入 AOF 文件來恢復數據。
只有當 AOF 關閉時,才會在 Redis 服務器啟動時檢測 RDB 文件,并自動載入。服務器載入 RDB 文件期間處于阻塞狀態,直到載入完成為止。
Redis 啟動日志中可以看到自動載入的執行:
Redis 載入 RDB 文件時,會對 RDB 文件進行校驗,如果文件損壞,則日志中會打印錯誤,Redis 啟動失敗。
RDB 常用配置總結
下面是 RDB 常用的配置項,以及默認值,前面介紹過的這里不再詳細介紹:
save m n:bgsave 自動觸發的條件;如果沒有 save m n 配置,相當于自動的 RDB 持久化關閉,不過此時仍可以通過其他方式觸發。
stop-writes-on-bgsave-error yes:當 bgsave 出現錯誤時,Redis 是否停止執行寫命令;設置為 yes,則當硬盤出現問題時,可以及時發現,避免數據的大量丟失。
設置為 no,則 Redis 無視 bgsave 的錯誤繼續執行寫命令,當對 Redis 服務器的系統(尤其是硬盤)使用了監控時,該選項考慮設置為 no。
rdbcompression yes:是否開啟 RDB 文件壓縮。
rdbchecksum yes:是否開啟 RDB 文件的校驗,在寫入文件和讀取文件時都起作用;關閉 checksum 在寫入文件和啟動文件時大約能帶來 10% 的性能提升,但是數據損壞時無法發現。
dbfilename dump.rdb:RDB 文件名。
dir ./:RDB 文件和 AOF 文件所在目錄。
AOF 持久化
RDB 持久化是將進程數據寫入文件,而 AOF 持久化(即 Append Only File 持久化),則是將 Redis 執行的每次寫命令記錄到單獨的日志文件中(有點像 MySQL 的 binlog),當 Redis 重啟時再次執行 AOF 文件中的命令來恢復數據。
與 RDB 相比,AOF 的實時性更好,因此已成為主流的持久化方案。
開啟 AOF
Redis 服務器默認開啟 RDB,關閉 AOF;要開啟 AOF,需要在配置文件中配置:appendonly yes。
執行流程
由于需要記錄 Redis 的每條寫命令,因此 AOF 不需要觸發,下面介紹 AOF 的執行流程。
AOF 的執行流程包括:
命令追加(append):將 Redis 的寫命令追加到緩沖區 aof_buf。
文件寫入(write)和文件同步(sync):根據不同的同步策略將 aof_buf 中的內容同步到硬盤。
文件重寫(rewrite):定期重寫 AOF 文件,達到壓縮的目的。
命令追加(append)
Redis 先將寫命令追加到緩沖區,而不是直接寫入文件,主要是為了避免每次有寫命令都直接寫入硬盤,導致硬盤 IO 成為 Redis 負載的瓶頸。
命令追加的格式是 Redis 命令請求的協議格式,它是一種純文本格式,具有兼容性好、可讀性強、容易處理、操作簡單避免二次開銷等優點,具體格式略。
在 AOF 文件中,除了用于指定數據庫的 select 命令(如 select 0 為選中 0 號數據庫)是由 Redis 添加的,其他都是客戶端發送來的寫命令。
文件寫入(write)和文件同步(sync)
Redis 提供了多種 AOF 緩存區的同步文件策略,策略涉及到操作系統的 write 函數和 fsync 函數,說明如下:
為了提高文件寫入效率,在現代操作系統中,當用戶調用 write 函數將數據寫入文件時,操作系統通常會將數據暫存到一個內存緩沖區里,當緩沖區被填滿或超過了指定時限后,才真正將緩沖區的數據寫入到硬盤里。
這樣的操作雖然提高了效率,但也帶來了安全問題:如果計算機停機,內存緩沖區中的數據會丟失。
因此系統同時提供了 fsync、fdatasync 等同步函數,可以強制操作系統立刻將緩沖區中的數據寫入到硬盤里,從而確保數據的安全性。
AOF 緩存區的同步文件策略由參數 appendfsync 控制,各個值的含義如下:
always:命令寫入 aof_buf 后立即調用系統 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)
隨著時間流逝,Redis 服務器執行的寫命令越來越多,AOF 文件也會越來越大;過大的 AOF 文件不僅會影響服務器的正常運行,也會導致數據恢復需要的時間過長。
文件重寫是指定期重寫 AOF 文件,減小 AOF 文件的體積。需要注意的是,AOF 重寫是把 Redis 進程內的數據轉化為寫命令,同步到新的 AOF 文件;不會對舊的 AOF 文件進行任何讀取、寫入操作!
關于文件重寫需要注意的另一點是:對于 AOF 持久化來說,文件重寫雖然是強烈推薦的,但并不是必須的。即使沒有文件重寫,數據也可以被持久化并在 Redis 啟動的時候導入。
因此在一些實現中,會關閉自動的文件重寫,然后通過定時任務在每天的某一時刻定時執行。
文件重寫之所以能夠壓縮 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 中定義,不可更改,3.0 版本中值是 64。
通過上述內容可以看出,由于重寫后 AOF 執行的命令減少了,文件重寫既可以減少文件占用的空間,也可以加快恢復速度。
文件重寫的觸發
文件重寫的觸發,分為手動觸發和自動觸發:
手動觸發,直接調用 bgrewriteaof 命令,該命令的執行與 bgsave 有些類似:都是 fork 子進程進行具體的工作,且都只有在 fork 時阻塞。
此時服務器執行日志如下:
自動觸發,根據 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 參數,以及 aof_current_size 和 aof_base_size 狀態確定觸發時機:
auto-aof-rewrite-min-size:執行 AOF 重寫時,文件的最小體積,默認值為 64MB。
auto-aof-rewrite-percentage:執行 AOF 重寫時,當前 AOF 大小(即 aof_current_size)和上一次重寫時 AOF 大小(aof_base_size)的比值。
其中,參數可以通過 config get 命令查看:
狀態可以通過 info persistence 查看:
只有當 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 兩個參數同時滿足時,才會自動觸發 AOF 重寫,即 bgrewriteaof 操作。
自動觸發 bgrewriteaof 時,可以看到服務器日志如下:
文件重寫的流程
文件重寫流程如下圖所示:
關于文件重寫的流程,有兩點需要特別注意:
重寫由父進程 fork 子進程進行。
重寫期間 Redis 執行的寫命令,需要追加到新的 AOF 文件中,為此 Redis 引入了 aof_rewrite_buf 緩存。
對照上圖,文件重寫的流程如下:
1):Redis 父進程首先判斷當前是否存在正在執行 bgsave/bgrewriteaof 的子進程,如果存在則 bgrewriteaof 命令直接返回;如果存在 bgsave 命令則等 bgsave 執行完成后再執行,這個主要是基于性能方面的考慮。
2):父進程執行 fork 操作創建子進程,這個過程中父進程是阻塞的。
3.1):父進程 fork 后,bgrewriteaof 命令返回“Background append only file rewrite started”信息并不再阻塞父進程,并可以響應其他命令。
Redis 的所有寫命令依然寫入 AOF 緩沖區,并根據 appendfsync 策略同步到硬盤,保證原有 AOF 機制的正確。
3.2):由于 fork 操作使用寫時復制技術,子進程只能共享 fork 操作時的內存數據。
由于父進程依然在響應命令,因此 Redis 使用 AOF 重寫緩沖區(圖中的 aof_rewrite_buf)保存這部分數據,防止新 AOF 文件生成期間丟失這部分數據。
也就是說,bgrewriteaof 執行期間,Redis 的寫命令同時追加到 aof_buf 和 aof_rewirte_buf 兩個緩沖區。
4):子進程根據內存快照,按照命令合并規則寫入到新的 AOF 文件。
5.1):子進程寫完新的 AOF 文件后,向父進程發信號,父進程更新統計信息,具體可以通過 info persistence 查看。
5.2):父進程把 AOF 重寫緩沖區的數據寫入到新的 AOF 文件,這樣就保證了新 AOF 文件所保存的數據庫狀態和服務器當前狀態一致。
5.3):使用新的 AOF 文件替換老文件,完成 AOF 重寫。
啟動時加載
前面提到過,當 AOF 開啟時,Redis 啟動時會優先載入 AOF 文件來恢復數據;只有當 AOF 關閉時,才會載入 RDB 文件恢復數據。
當 AOF 開啟,且 AOF 文件存在時,Redis 啟動日志:
當 AOF 開啟,但 AOF 文件不存在時,即使 RDB 文件存在也不會加載(更早的一些版本可能會加載,但 3.0 不會),Redis 啟動日志如下:
文件校驗
與載入 RDB 文件類似,Redis 載入 AOF 文件時,會對 AOF 文件進行校驗,如果文件損壞,則日志中會打印錯誤,Redis 啟動失敗。
但如果是 AOF 文件結尾不完整(機器突然宕機等容易導致文件尾部不完整),且 aof-load-truncated 參數開啟,則日志中會輸出警告,Redis 忽略掉 AOF 文件的尾部,啟動成功。
aof-load-truncated 參數默認是開啟的:
偽客戶端
因為 Redis 的命令只能在客戶端上下文中執行,而載入 AOF 文件時命令是直接從文件中讀取的,并不是由客戶端發送。
因此 Redis 服務器在載入 AOF 文件之前,會創建一個沒有網絡連接的客戶端,之后用它來執行 AOF 文件中的命令,命令執行的效果與帶網絡連接的客戶端完全一樣。
AOF 常用配置總結
下面是 AOF 常用的配置項,以及默認值:
appendonly no:是否開啟 AOF。
appendfilename "appendonly.aof":AOF 文件名。
dir ./:RDB 文件和 AOF 文件所在目錄。
appendfsync everysec:fsync 持久化策略。
no-appendfsync-on-rewrite no:AOF 重寫期間是否禁止 fsync;如果開啟該選項,可以減輕文件重寫時 CPU 和硬盤的負載(尤其是硬盤),但是可能會丟失 AOF 重寫期間的數據;需要在負載和安全性之間進行平衡。
auto-aof-rewrite-percentage 100:文件重寫觸發條件之一。
auto-aof-rewrite-min-size 64mb:文件重寫觸發提交之一。
aof-load-truncated yes:如果 AOF 文件結尾損壞,Redis 啟動時是否仍載入 AOF 文件。
方案選擇與常見問題
前面介紹了 RDB 和 AOF 兩種持久化方案的細節,下面介紹 RDB 和 AOF 的特點、如何選擇持久化方案,以及在持久化過程中常遇到的問題等。
RDB 和 AOF 的優缺點
RDB 和 AOF 各有優缺點:
RDB 持久化
優點:RDB 文件緊湊,體積小,網絡傳輸快,適合全量復制;恢復速度比 AOF 快很多。當然,與 AOF 相比,RDB 最重要的優點之一是對性能的影響相對較小。
缺點:RDB 文件的致命缺點在于其數據快照的持久化方式決定了必然做不到實時持久化,而在數據越來越重要的今天,數據的大量丟失很多時候是無法接受的,因此 AOF 持久化成為主流。
此外,RDB 文件需要滿足特定格式,兼容性差(如老版本的 Redis 不兼容新版本的 RDB 文件)。
AOF 持久化
與 RDB 持久化相對應,AOF 的優點在于支持秒級持久化、兼容性好,缺點是文件大、恢復速度慢、對性能影響大。
持久化策略選擇
在介紹持久化策略之前,首先要明白無論是 RDB 還是 AOF,持久化的開啟都是要付出性能方面代價的:
對于 RDB 持久化,一方面是 bgsave 在進行 fork 操作時 Redis 主進程會阻塞,另一方面,子進程向硬盤寫數據也會帶來 IO 壓力。
對于 AOF 持久化,向硬盤寫數據的頻率大大提高(everysec 策略下為秒級),IO 壓力更大,甚至可能造成 AOF 追加阻塞問題(后面會詳細介紹這種阻塞)。
此外,AOF 文件的重寫與 RDB 的 bgsave 類似,會有 fork 時的阻塞和子進程的 IO 壓力問題。
相對來說,由于 AOF 向硬盤中寫數據的頻率更高,因此對 Redis 主進程性能的影響會更大。
在實際生產環境中,根據數據量、應用對數據的安全要求、預算限制等不同情況,會有各種各樣的持久化策略。
如完全不使用任何持久化、使用 RDB 或 AOF 的一種,或同時開啟 RDB 和 AOF 持久化等。
此外,持久化的選擇必須與 Redis 的主從策略一起考慮,因為主從復制與持久化同樣具有數據備份的功能,而且主機 master 和從機 slave 可以獨立的選擇持久化方案。
下面分場景來討論持久化策略的選擇,討論也只是作為參考,實際方案可能更復雜更具多樣性:
如果 Redis 中的數據完全丟棄也沒有關系(如 Redis 完全用作 DB 層數據的 Cache),那么無論是單機,還是主從架構,都可以不進行任何持久化。
在單機環境下(對于個人開發者,這種情況可能比較常見),如果可以接受十幾分鐘或更多的數據丟失,選擇 RDB 對 Redis 的性能更加有利;如果只能接受秒級別的數據丟失,應該選擇 AOF。
但在多數情況下,我們都會配置主從環境,slave 的存在既可以實現數據的熱備,也可以進行讀寫分離分擔 Redis 讀請求,以及在 master 宕掉后繼續提供服務。
在這種情況下,一種可行的做法是:
master:完全關閉持久化(包括 RDB 和 AOF),這樣可以讓 master 的性能達到***。
slave:關閉 RDB,開啟 AOF(如果對數據安全要求不高,開啟 RDB 關閉 AOF 也可以),并定時對持久化文件進行備份(如備份到其他文件夾,并標記好備份的時間)。
然后關閉 AOF 的自動重寫,然后添加定時任務,在每天 Redis 閑時(如凌晨 12 點)調用 bgrewriteaof。
這里需要解釋一下,為什么開啟了主從復制,可以實現數據的熱備份,還需要設置持久化呢?
因為在一些特殊情況下,主從復制仍然不足以保證數據的安全,例如:
master 和 slave 進程同時停止:考慮這樣一種場景,如果 master 和 slave 在同一棟大樓或同一個機房,則一次停電事故就可能導致 master 和 slave 機器同時關機,Redis 進程停止;如果沒有持久化,則面臨的是數據的完全丟失。
master誤重啟:考慮這樣一種場景,master 服務因為故障宕掉了,如果系統中有自動拉起機制(即檢測到服務停止后重啟該服務)將 master 自動重啟,由于沒有持久化文件,那么 master 重啟后數據是空的,slave 同步數據也變成了空的;如果 master 和 slave 都沒有持久化,同樣會面臨數據的完全丟失。
需要注意的是,即便是使用了哨兵(關于哨兵后面會有文章介紹)進行自動的主從切換,也有可能在哨兵輪詢到 master 之前,便被自動拉起機制重啟了。因此,應盡量避免“自動拉起機制”和“不做持久化”同時出現。
異地災備:上述討論的幾種持久化策略,針對的都是一般的系統故障,如進程異常退出、宕機、斷電等,這些故障不會損壞硬盤。
但是對于一些可能導致硬盤損壞的災難情況,如火災地震,就需要進行異地災備。
例如對于單機的情形,可以定時將 RDB 文件或重寫后的 AOF 文件,通過 scp 拷貝到遠程機器,如阿里云、AWS 等。
對于主從的情形,可以定時在 master 上執行 bgsave,然后將 RDB 文件拷貝到遠程機器,或者在 slave 上執行 bgrewriteaof 重寫 AOF 文件后,將 AOF 文件拷貝到遠程機器上。
一般來說,由于 RDB 文件文件小、恢復快,因此災難恢復常用 RDB 文件;異地備份的頻率根據數據安全性的需要及其他條件來確定,但***不要低于一天一次。
fork 阻塞:CPU 的阻塞
在 Redis 的實踐中,眾多因素限制了 Redis 單機的內存不能過大,例如:
當面對請求的暴增,需要從庫擴容時,Redis 內存過大會導致擴容時間太長。
當主機宕機時,切換主機后需要掛載從庫,Redis 內存過大導致掛載速度過慢。
持久化過程中的 fork 操作。
首先說明一下 fork 操作:父進程通過 fork 操作可以創建子進程;子進程創建后,父子進程共享代碼段,不共享進程的數據空間,但是子進程會獲得父進程的數據空間的副本。
在操作系統 fork 的實際實現中,基本都采用了寫時復制技術,即在父/子進程試圖修改數據空間之前,父子進程實際上共享數據空間。
但是當父/子進程的任何一個試圖修改數據空間時,操作系統會為修改的那一部分(內存的一頁)制作一個副本。
雖然 fork 時,子進程不會復制父進程的數據空間,但是會復制內存頁表(頁表相當于內存的索引、目錄);父進程的數據空間越大,內存頁表越大,fork 時復制耗時也會越多。
在 Redis 中,無論是 RDB 持久化的 bgsave,還是 AOF 重寫的 bgrewriteaof,都需要 fork 出子進程來進行操作。
如果 Redis 內存過大,會導致 fork 操作時復制內存頁表耗時過多;而 Redis 主進程在進行 fork 時,是完全阻塞的,也就意味著無法響應客戶端的請求,會造成請求延遲過大。
對于不同的硬件、不同的操作系統,fork 操作的耗時會有所差別,一般來說,如果 Redis 單機內存達到了 10GB,fork 時耗時可能會達到百毫秒級別(如果使用 Xen 虛擬機,這個耗時可能達到秒級別)。
因此,一般來說 Redis 單機內存一般要限制在 10GB 以內;不過這個數據并不是絕對的,可以通過觀察線上環境 fork 的耗時來進行調整。
觀察的方法如下:執行命令 info stats,查看 latest_fork_usec 的值,單位為微秒。
為了減輕 fork 操作帶來的阻塞問題,除了控制 Redis 單機內存的大小以外,還可以適度放寬 AOF 重寫的觸發條件、選用物理機或高效支持 fork 操作的虛擬化技術等,例如使用 Vmware 或 KVM 虛擬機,不要使用 Xen 虛擬機。
AOF 追加阻塞:硬盤的阻塞
前面提到過,在 AOF 中,如果 AOF 緩沖區的文件同步策略為 everysec,則在主線程中,命令寫入 aof_buf 后調用系統 write 操作,write 完成后主線程返回。
fsync 同步文件操作由專門的文件同步線程每秒調用一次。這種做法的問題在于,如果硬盤負載過高,那么 fsync 操作可能會超過 1s。
如果 Redis 主線程持續高速向 aof_buf 寫入命令,硬盤的負載可能會越來越大,IO 資源消耗更快;如果此時 Redis 進程異常退出,丟失的數據也會越來越多,可能遠超過 1s。
為此,Redis 的處理策略是這樣的:主線程每次進行 AOF 會對比上次 fsync 成功的時間;如果距上次不到 2s,主線程直接返回;如果超過 2s,則主線程阻塞直到 fsync 同步完成。
因此,如果系統硬盤負載過大導致 fsync 速度太慢,會導致 Redis 主線程的阻塞;此外,使用 everysec 配置,AOF 最多可能丟失 2s 的數據,而不是 1s。
AOF 追加阻塞問題定位的方法:
監控 info Persistence 中的 aof_delayed_fsync:當 AOF 追加阻塞發生時(即主線程等待 fsync 而阻塞),該指標累加。
AOF 阻塞時的 Redis 日志:Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.
如果 AOF 追加阻塞頻繁發生,說明系統的硬盤負載太大,可以考慮更換 IO 速度更快的硬盤,或者通過 IO 監控分析工具對系統的 IO 負載進行分析,如 iostat(系統級 io)、iotop(io 版的 top)、pidstat 等。
info 命令與持久化
前面提到了一些通過 info 命令查看持久化相關狀態的方法,下面來總結一下。
info Persistence
執行結果如下:
其中比較重要的包括:
rdb_last_bgsave_status:上次 bgsave 執行結果,可以用于發現 bgsave 錯誤。
rdb_last_bgsave_time_sec:上次 bgsave 執行時間(單位是 s),可以用于發現 bgsave 是否耗時過長。
aof_enabled:AOF 是否開啟。
aof_last_rewrite_time_sec:上次文件重寫執行時間(單位是 s),可以用于發現文件重寫是否耗時過長。
aof_last_bgrewrite_status:上次 bgrewrite 執行結果,可以用于發現 bgrewrite 錯誤。
aof_buffer_length 和 aof_rewrite_buffer_length:AOF 緩存區大小和 AOF 重寫緩沖區大小。
aof_delayed_fsync:AOF 追加阻塞情況的統計。
info stats
其中與持久化關系較大的是:latest_fork_usec,代表上次 fork 耗時。
關于“Redis的高可用特性持久化怎么實現”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。