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