redis(三:日志二)

正因為AOF記錄的是操作命令,而不是實際的數(shù)據(jù),所以,用 AOF 方法進(jìn)行故障恢復(fù)的時候,需要逐一把操作日志都執(zhí)行一遍。而RDB(Redis DataBase)內(nèi)存快照能在宕機(jī)時實現(xiàn)快速恢復(fù)。所謂內(nèi)存快照,就是指內(nèi)存中的數(shù)據(jù)在某一個時刻的狀態(tài)記錄。

和 AOF 相比,RDB 記錄的是某一時刻的數(shù)據(jù),并不是操作,所以,在做數(shù)據(jù)恢復(fù)時,我們可以直接把 RDB 文件讀入內(nèi)存,很快地完成恢復(fù)。全量數(shù)據(jù)越多,RDB 文件就越大,往磁盤上寫數(shù)據(jù)的時間開銷就越大。

Redis 提供了兩個命令來生成 RDB 文件,分別是 save 和 bgsave。
1,save:在主線程中執(zhí)行,會導(dǎo)致阻塞;
2,bgsave:創(chuàng)建一個子進(jìn)程,專門用于寫入 RDB 文件,避免了主線程的阻塞,這也是 Redis RDB 文件生成的默認(rèn)配置。

我們就可以通過 bgsave 命令來執(zhí)行全量快照,不會阻塞主線程。這既提供了數(shù)據(jù)的可靠性保證,也避免了對 Redis 的性能影響。

快照時數(shù)據(jù)能修改嗎

此時,主線程的確沒有阻塞,可以正常接收請求,但是,為了保證快照完整性,它只能處理讀操作,因為不能修改正在執(zhí)行快照的數(shù)據(jù)。
為了快照而暫停寫操作,肯定是不能接受的。所以這個時候,Redis 就會借助操作系統(tǒng)提供的寫時復(fù)制技術(shù)(Copy-On-Write, COW),在執(zhí)行快照的同時,正常處理寫操作

此時,如果主線程對這些數(shù)據(jù)也都是讀操作(例如圖中的鍵值對 A),那么,主線程和 bgsave 子進(jìn)程相互不影響。但是,如果主線程要修改一塊數(shù)據(jù)(例如圖中的鍵值對 C),那么,這塊數(shù)據(jù)就會被復(fù)制一份,生成該數(shù)據(jù)的副本(鍵值對 C’)。然后,主線程在這個數(shù)據(jù)副本上進(jìn)行修改。同時,bgsave 子進(jìn)程可以繼續(xù)把原來的數(shù)據(jù)(鍵值對 C)寫入 RDB 文件。



這既保證了快照的完整性,也允許主線程同時對數(shù)據(jù)進(jìn)行修改,避免了對正常業(yè)務(wù)的影響。

快照頻率
是不是可以每秒做一次快照?畢竟,每次快照都是由 bgsave 子進(jìn)程在后臺執(zhí)行,也不會阻塞主線程。

如果頻繁地執(zhí)行全量快照,也會帶來兩方面的開銷。
一方面,頻繁將全量數(shù)據(jù)寫入磁盤,會給磁盤帶來很大壓力,多個快照競爭有限的磁盤帶寬,前一個快照還沒有做完,后一個又開始做了,容易造成惡性循環(huán)。
另一方面,bgsave 子進(jìn)程需要通過 fork 操作從主線程創(chuàng)建出來。雖然,子進(jìn)程在創(chuàng)建后不會再阻塞主線程,但是,fork 這個創(chuàng)建過程本身會阻塞主線程,而且主線程的內(nèi)存越大,阻塞時間越長。

增量快照
做了一次全量快照后,后續(xù)的快照只對修改的數(shù)據(jù)進(jìn)行快照記錄,這樣可以避免每次全量快照的開銷。
但是,這么做的前提是,我們需要記住哪些數(shù)據(jù)被修改了。你可不要小瞧這個“記住”功能,它需要我們使用額外的元數(shù)據(jù)信息去記錄哪些數(shù)據(jù)被修改了,這會帶來額外的空間開銷問題。


雖然跟 AOF 相比,快照的恢復(fù)速度快,但是,快照的頻率不好把握,如果頻率太低,兩次快照間一旦宕機(jī),就可能有比較多的數(shù)據(jù)丟失。如果頻率太高,又會產(chǎn)生額外開銷

混合日志
Redis 4.0 中提出了一個混合使用 AOF 日志和內(nèi)存快照的方法。簡單來說,內(nèi)存快照以一定的頻率執(zhí)行,在兩次快照之間,使用 AOF 日志記錄這期間的所有命令操作。
這樣一來,快照不用很頻繁地執(zhí)行,這就避免了頻繁 fork 對主線程的影響。而且,AOF 日志也只用記錄兩次快照間的操作,也就是說,不需要記錄所有操作了,因此,就不會出現(xiàn)文件過大的情況了,也可以避免重寫開銷。
如下圖所示,T1 和 T2 時刻的修改,用 AOF 日志記錄,等到第二次做全量快照時,就可以清空 AOF 日志,因為此時的修改都已經(jīng)記錄到快照中了,恢復(fù)時就不再用日志了。

關(guān)于 AOF 和 RDB 的選擇問題,有三點建議:
數(shù)據(jù)不能丟失時,內(nèi)存快照和 AOF 的混合使用是一個很好的選擇;
如果允許分鐘級別的數(shù)據(jù)丟失,可以只使用 RDB;
如果只用 AOF,優(yōu)先使用 everysec 的配置選項,因為它在可靠性和性能之間取了一個平衡。

RDB層面解釋一個redis實例內(nèi)存為何不宜過大?
RDB fork時會產(chǎn)生比較長時間阻塞。
RDB 進(jìn)行持久化時,Redis 會 fork 子進(jìn)程來完成,fork 操作的用時和 Redis 的數(shù)據(jù)量是正相關(guān)的,而 fork 在執(zhí)行時會阻塞主線程。數(shù)據(jù)量越大,fork 操作造成的主線程阻塞的時間越長。

我們使用一個 2 核 CPU、4GB 內(nèi)存、500GB 磁盤的云主機(jī)運(yùn)行 Redis,Redis 數(shù)據(jù)庫的數(shù)據(jù)量大小差不多是 2GB,我們使用了 RDB 做持久化保證。當(dāng)時 Redis 的運(yùn)行負(fù)載以修改操作為主,寫讀比例差不多在 8:2 左右,也就是說,如果有 100 個請求,80 個請求執(zhí)行的是修改操作。你覺得,在這個場景下,用 RDB 做持久化有什么風(fēng)險嗎?你能幫著一起分析分析嗎?
2核CPU、4GB內(nèi)存、500G磁盤,Redis實例占用2GB,寫讀比例為8:2,此時做RDB持久化,產(chǎn)生的風(fēng)險主要在于 CPU資源 和 內(nèi)存資源 這2方面:
a、內(nèi)存資源風(fēng)險:Redis fork子進(jìn)程做RDB持久化,由于寫的比例為80%,那么在持久化過程中,“寫實復(fù)制”會重新分配整個實例80%的內(nèi)存副本,大約需要重新分配1.6GB內(nèi)存空間,這樣整個系統(tǒng)的內(nèi)存使用接近飽和,如果此時父進(jìn)程又有大量新key寫入,很快機(jī)器內(nèi)存就會被吃光,如果機(jī)器開啟了Swap機(jī)制,那么Redis會有一部分?jǐn)?shù)據(jù)被換到磁盤上,當(dāng)Redis訪問這部分在磁盤上的數(shù)據(jù)時,性能會急劇下降,已經(jīng)達(dá)不到高性能的標(biāo)準(zhǔn)(可以理解為武功被廢)。如果機(jī)器沒有開啟Swap,會直接觸發(fā)OOM,父子進(jìn)程會面臨被系統(tǒng)kill掉的風(fēng)險。
b、CPU資源風(fēng)險:雖然子進(jìn)程在做RDB持久化,但生成RDB快照過程會消耗大量的CPU資源,雖然Redis處理處理請求是單線程的,但Redis Server還有其他線程在后臺工作,例如AOF每秒刷盤、異步關(guān)閉文件描述符這些操作。由于機(jī)器只有2核CPU,這也就意味著父進(jìn)程占用了超過一半的CPU資源,此時子進(jìn)程做RDB持久化,可能會產(chǎn)生CPU競爭,導(dǎo)致的結(jié)果就是父進(jìn)程處理請求延遲增大,子進(jìn)程生成RDB快照的時間也會變長,整個Redis Server性能下降。

分析問題時還可以考慮下其他影響:QPS較低,不會有什么問題,快照時間和頻率。晚上和一天一次也未嘗不可

摘抄:《Redis 核心技術(shù)與實戰(zhàn)》-第5節(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。