緩存數(shù)據(jù)丟了,原來(lái)是Redis持久化沒(méi)玩明白
我們都知道Redis是微服務(wù)架構(gòu)中重要的基礎(chǔ)數(shù)據(jù)庫(kù)中間件,通過(guò)Redis可以將數(shù)據(jù)庫(kù)中的數(shù)據(jù)緩存到內(nèi)存中,當(dāng)服務(wù)端有數(shù)據(jù)查詢(xún)請(qǐng)求的時(shí)候,可以直接從內(nèi)存中獲取數(shù)據(jù)。如此,一方面服務(wù)端可以獲得比較快的數(shù)據(jù)請(qǐng)求響應(yīng),另一方面降低了后端關(guān)系數(shù)據(jù)庫(kù)的業(yè)務(wù)請(qǐng)求壓力。但是正所謂尺有所短,寸有所長(zhǎng),Redis最大的優(yōu)勢(shì)就是內(nèi)存數(shù)據(jù)也是最大的劣勢(shì),因?yàn)橐坏┓?wù)器宕機(jī)或者服務(wù)器重啟,內(nèi)存中緩存的數(shù)據(jù)也會(huì)丟失。針對(duì)這樣的場(chǎng)景,Redis提供了三種數(shù)據(jù)持久化機(jī)制,分別是AOF、RDB以及混合持久化來(lái)應(yīng)對(duì)這種異常情況。本文主要從Redis實(shí)現(xiàn)持久化遇到的問(wèn)題出發(fā),站在設(shè)計(jì)者的角度思考相關(guān)問(wèn)題的解決思路。
?
AOF持久化
AOF持久化方式,即Append Only File,Redis通過(guò)記錄執(zhí)行修改操作命令這種記小本本的方式進(jìn)行內(nèi)存數(shù)據(jù)持久化。當(dāng)需要通過(guò)AOF日志進(jìn)行恢復(fù)數(shù)據(jù)時(shí),Redis服務(wù)端啟動(dòng)后可以從日志文件中回放執(zhí)行命令來(lái)實(shí)現(xiàn)內(nèi)存數(shù)據(jù)恢復(fù)。當(dāng)然了,AOF日志中記錄的都是修改的命令,查詢(xún)命令不會(huì)修改數(shù)據(jù)所以不需要進(jìn)行記錄。
可能大家都比較熟悉WAL(Write Ahead Log),即日志預(yù)寫(xiě)機(jī)制,它是數(shù)據(jù)庫(kù)非常常用的確保數(shù)據(jù)操作原子性以及持久性的技術(shù)手段。拿Mysql舉栗子,Mysql的WAL體現(xiàn)在undo log以及redo log等這些日志文件中,數(shù)據(jù)庫(kù)在執(zhí)行修改操作的時(shí)候并不是立刻將數(shù)據(jù)更新到磁盤(pán)上,而是先記錄在日志中,主要目的是如果出現(xiàn)異常,可以直接從redo log中進(jìn)行數(shù)據(jù)恢復(fù),也就是說(shuō)讓Mysql知道上次意外發(fā)生的時(shí)候操作到底有沒(méi)有成功,另外還可以將Mysql的隨機(jī)寫(xiě)轉(zhuǎn)換為順序?qū)懀嵘齀O性能。但是AOF卻不同,它是在Redis將數(shù)據(jù)寫(xiě)入內(nèi)存之后,再將相關(guān)的操作命令寫(xiě)入AOF文件中。
那么問(wèn)題來(lái)了,為什么Redis要采取這種獨(dú)特的數(shù)據(jù)記錄方式,而不是業(yè)界常用的WAL的方式呢?其實(shí)可以從以下兩個(gè)層面思考原因。
(1)AOF文件中保存了執(zhí)行緩存的命令,以便于保證在需要恢復(fù)數(shù)據(jù)的時(shí)候可以進(jìn)行命令重放恢復(fù)數(shù)據(jù),因此需要保證執(zhí)行命令的合法性,而通過(guò)先緩存數(shù)據(jù)再進(jìn)行命令追加日志的方式可以確保追加到AOF文件中的的命令都是合法有效的,redis在恢復(fù)數(shù)據(jù)的時(shí)候不需要再去檢查命令是否有效,進(jìn)一步提升內(nèi)存數(shù)據(jù)恢復(fù)的效率。
(2)另外由于是在修改操作命令之后進(jìn)行日志記錄,日志記錄的時(shí)候需要進(jìn)行磁盤(pán)IO操作,因此不會(huì)阻塞當(dāng)前的修改命令。
AOF文件內(nèi)容是什么?
在搞清楚Redis為什么采用AOF文件記錄修改命令之后,我們?cè)賮?lái)看看AOF文件中到底包含了哪些內(nèi)容。
Redis客戶(hù)端與服務(wù)端之間采用RESP協(xié)議進(jìn)行通信,它是一種應(yīng)用層協(xié)議,對(duì)于Redis這種以效率為追求目標(biāo)的中間件,通信協(xié)議必定要簡(jiǎn)單高效。就上面一條緩存操作命令來(lái)說(shuō):set mufeng handsome 對(duì)應(yīng)的RESP報(bào)文就是*3$3set$6mufeng$8handsome,為了方便查看進(jìn)行了手動(dòng)換行。
我們來(lái)拆解下報(bào)文中各個(gè)屬性的含義,“*3”代表本次操作命令將由三個(gè)分布組成,每一部分都是通過(guò)"$數(shù)字"的形式作為起始,后面為對(duì)應(yīng)的命令、鍵或者值。如此處的"$6"就表示后面的命令是一個(gè)6個(gè)字節(jié)的鍵值。所以,appenonly.aof文件中實(shí)際保存的就是這種格式的內(nèi)容。
AOF有沒(méi)有丟數(shù)據(jù)的風(fēng)險(xiǎn)?
上文說(shuō)到Redis通過(guò)AOF文件實(shí)現(xiàn)內(nèi)存數(shù)據(jù)持久化,那么是不是就代表緩存數(shù)據(jù)保存就萬(wàn)無(wú)一失了?這樣的持久化方式還有沒(méi)有數(shù)據(jù)丟失的風(fēng)險(xiǎn)呢?大家可以設(shè)想一下假設(shè)在操作完Redis之后,還沒(méi)來(lái)得及將命令寫(xiě)入AOF文件就宕機(jī)了,那么這個(gè)操作命令就會(huì)丟失,對(duì)應(yīng)的緩存數(shù)據(jù)最新值也會(huì)丟失。因?yàn)榧幢沐礄C(jī)異常恢復(fù)之后,也沒(méi)辦法從AOF文件中執(zhí)行丟失的操作命令了。因此,寫(xiě)入AOF緩沖區(qū)的數(shù)據(jù)什么時(shí)候進(jìn)行持久化落盤(pán),直接決定著AOF持久化方式緩存數(shù)據(jù)丟失的風(fēng)險(xiǎn)大小。
三種AOF落盤(pán)策略
針對(duì)AOF緩存中的數(shù)據(jù)在什么時(shí)機(jī)寫(xiě)入磁盤(pán),Redis提供了三種AOF日志寫(xiě)入策略供用戶(hù)進(jìn)行選擇,通過(guò)后臺(tái)線程執(zhí)行不同時(shí)機(jī)的AOF文件數(shù)據(jù)同步操作,在redis.conf配置文件中的配置項(xiàng)appendfsync可以進(jìn)行配置。
【appendfsync:no】?
Redis不用管AOF緩沖區(qū)的數(shù)據(jù)什么時(shí)候?qū)懭氪疟P(pán),將AOF緩沖區(qū)同步數(shù)據(jù)的操作權(quán)交給操作系統(tǒng),操作系統(tǒng)決定什么時(shí)候?qū)⒕彌_區(qū)的數(shù)據(jù)寫(xiě)入磁盤(pán)中。
【appendfsync:everysec】
當(dāng)Redis將數(shù)據(jù)寫(xiě)入AOF緩沖區(qū)后,每隔1s將緩沖區(qū)的數(shù)據(jù)進(jìn)行磁盤(pán)寫(xiě)入。
【appendfsync:always】?
每執(zhí)行一個(gè)修改命令,都需要將修改的命令進(jìn)行落盤(pán)操作。
雖然Redis提供了這三種AOF日志落盤(pán)策略供用戶(hù)進(jìn)行選擇,但是這三種策略實(shí)際上各有優(yōu)缺點(diǎn)。
【appendfsync:no】
如果設(shè)置了由操作系統(tǒng)進(jìn)行AOF緩沖區(qū)數(shù)據(jù)寫(xiě)入,那么就相當(dāng)于寫(xiě)數(shù)據(jù)的時(shí)機(jī)完全交由操作系統(tǒng)來(lái)決定,此時(shí)redis對(duì)于緩沖區(qū)數(shù)據(jù)并不可以控制。
【appendfsync:everysec】
如果設(shè)置成每隔一秒進(jìn)行緩存數(shù)據(jù)寫(xiě)入,雖然不會(huì)像同步寫(xiě)入那樣存在一定的性能消耗,但是由于存在一秒的時(shí)間間隔,如果在此期間出現(xiàn)服務(wù)器宕機(jī),那么就會(huì)損失這一秒的緩存數(shù)據(jù)。
【appendfsync:always】
雖然可以基本實(shí)現(xiàn)數(shù)據(jù)不丟失,但是由于每次進(jìn)行內(nèi)存數(shù)據(jù)修改都要進(jìn)行落盤(pán)操作,因此在一定程度上會(huì)影響主線程性能。
具體采取怎樣的配置策略還是要根據(jù)實(shí)際的業(yè)務(wù)場(chǎng)景來(lái)決定,一般推薦使用第二種配置策略【appendfsync:everysec】,在可靠性以及性能方面相對(duì)平衡一點(diǎn)。
AOF文件會(huì)越來(lái)越大嗎?
在了解了AOF日志磁盤(pán)寫(xiě)入時(shí)機(jī)之后,我們繼續(xù)來(lái)思考下一個(gè)問(wèn)題。無(wú)論采取什么樣的同步數(shù)據(jù)策略,最終都是要將修改命令寫(xiě)入AOF文件中,因此隨著時(shí)間的推移,這個(gè)文件必定會(huì)越來(lái)越大。那么如果文件變得很大之后,無(wú)論是文件數(shù)據(jù)新寫(xiě)入還是Redis通過(guò)AOF文件進(jìn)行數(shù)據(jù)恢復(fù),大文件的操作都會(huì)造成IO性能損耗。假如你是Redis的設(shè)計(jì)者,如果遇到這種情況你會(huì)怎么進(jìn)行設(shè)計(jì)優(yōu)化呢?我想無(wú)非有兩個(gè)優(yōu)化思路,一個(gè)是化整為零,一個(gè)是想辦法縮小大文件。
化整為零
當(dāng)單個(gè)文件過(guò)大時(shí),我們很容易想到的優(yōu)化方法就是將這個(gè)大文件拆分為若干個(gè)小文件。這就好比系統(tǒng)中一旦出現(xiàn)過(guò)千萬(wàn)數(shù)據(jù)庫(kù)表的時(shí)候,我們就要結(jié)合實(shí)際的業(yè)務(wù)場(chǎng)景考慮要不要進(jìn)行分庫(kù)分表了。所以如果單個(gè)AOF文件太大,那么是不是可以考慮將其按照固定大小進(jìn)行拆分,這樣可以避免單個(gè)AOF文件過(guò)大的問(wèn)題。那么Redis小于7.0版本為什么沒(méi)有采用這種方案呢?主要是這種方案并不符合Redis追求簡(jiǎn)單高效的設(shè)計(jì)思想。假設(shè)采用了這種數(shù)據(jù)分塊的方式,那必定需要實(shí)現(xiàn)文件大小檢測(cè)、文件創(chuàng)建、文件索引維護(hù)等等一系列技術(shù)細(xì)節(jié)問(wèn)題,對(duì)于低版本的Redis來(lái)說(shuō)這些都太繁瑣了,還不如一個(gè)AOF文件來(lái)的爽快。
PS:在最新的Redis 7.0版本中,Redis已經(jīng)支持多AOF文件分片機(jī)制,原始的單個(gè)AOF文件會(huì)被拆分為一個(gè)基礎(chǔ)文件以及多個(gè)增量文件。新版本中之所以開(kāi)始支持多文件存儲(chǔ),我想也是隨著業(yè)務(wù)發(fā)展內(nèi)存數(shù)據(jù)可能會(huì)很龐大,Redis設(shè)計(jì)者發(fā)現(xiàn)如果還是使用單文件存儲(chǔ),大AOF文件操作以及數(shù)據(jù)恢復(fù)都是一個(gè)挑戰(zhàn)。
AOF重寫(xiě)
既然進(jìn)行文件切割太繁瑣了,那么就單個(gè)AOF文件來(lái)說(shuō)怎么才能減小文件大小呢?那就要從AOF文件的記錄內(nèi)容入手,通過(guò)上文我們了解到AOF文件中實(shí)際存儲(chǔ)了修改內(nèi)存數(shù)據(jù)的操作命令,因此我們?cè)诜治鐾赀@些操作命令之后發(fā)現(xiàn),當(dāng)多條命令操作同一個(gè)key的時(shí)候,實(shí)際我們需要的是最新的一條操作命令,除此之外的歷史操作命令我們并不需要關(guān)心。比如【set mufeng handsome】、【set mufeng cool】,如果先后執(zhí)行了這兩個(gè)命令,那么在最終恢復(fù)數(shù)據(jù)的時(shí)候,只要恢復(fù)【set mufeng cool】即可。因此AOF重寫(xiě)的本質(zhì)就是合并命令,也就是說(shuō)將多條對(duì)同一key進(jìn)行操作的命令進(jìn)行合并,實(shí)際就是使用最新的key值操作命令來(lái)代替之前所有關(guān)于這個(gè)key值的命令。
Redis通過(guò)fork子進(jìn)程來(lái)完成AOF文件重寫(xiě),因此在講AOF重寫(xiě)過(guò)程之前,我們需要先了解下什么是fork子進(jìn)程的原理,這樣更加有利于我們后面了解AOF文件重寫(xiě)的過(guò)程。
什么是fork?
fork函數(shù)是linux內(nèi)核提供給用戶(hù)創(chuàng)建進(jìn)程的API,應(yīng)用程序通過(guò)調(diào)用fork函數(shù)創(chuàng)建子進(jìn)程,這個(gè)子進(jìn)程可以和原來(lái)父進(jìn)程干同樣的事情,也可以和原來(lái)主進(jìn)程干不同的事情,這主要取決于對(duì)應(yīng)的參數(shù)。這個(gè)過(guò)程就好比孫悟空拔了一根自己的猴毛變出來(lái)一個(gè)和自己一模一樣的孫悟空。
因此在fork子進(jìn)程的過(guò)程之中,子進(jìn)程復(fù)制了父進(jìn)程的代碼段、數(shù)據(jù)段、堆棧、頁(yè)表等,同時(shí)子進(jìn)程擁有獨(dú)立的虛擬內(nèi)存空間(當(dāng)然是從父進(jìn)程那里復(fù)制過(guò)來(lái)的)。如下所示,實(shí)際上fork()最終調(diào)用的是內(nèi)核copy_process方法復(fù)制進(jìn)程。?
父進(jìn)程fork子進(jìn)程的時(shí)候,子進(jìn)程擁有獨(dú)立的虛擬內(nèi)存空間,那么對(duì)應(yīng)的物理內(nèi)存空間是不是也是獨(dú)立的呢?我們都知道在計(jì)算機(jī)中,內(nèi)存屬于非常寶貴的系統(tǒng)資源,所以大佬們?cè)谠O(shè)計(jì)的時(shí)候都盡可能的減少內(nèi)存空間占用從而提高系統(tǒng)資源利用率。fork子進(jìn)程過(guò)程中用到的Copy-On-Write就是典型的內(nèi)存資源管理優(yōu)化機(jī)制,如果子進(jìn)程只是讀取數(shù)據(jù)不進(jìn)行任何的數(shù)據(jù)寫(xiě)入,那么就和父進(jìn)程公用內(nèi)存空間。當(dāng)子進(jìn)程需要進(jìn)行數(shù)據(jù)寫(xiě)入的時(shí)候,發(fā)現(xiàn)沒(méi)有內(nèi)控空間可以寫(xiě)入,此時(shí)會(huì)觸發(fā)一個(gè)系統(tǒng)中斷來(lái)分配內(nèi)存空間給子進(jìn)程進(jìn)行數(shù)據(jù)寫(xiě)入。
什么時(shí)機(jī)觸發(fā)AOF重寫(xiě)?
執(zhí)行bgrewriteaof 命令
當(dāng)我們?cè)诳蛻?hù)端手動(dòng)執(zhí)行bgrewriteaof 命令后,可以觸發(fā)AOF文件進(jìn)行重寫(xiě),對(duì)應(yīng)Redis源碼中進(jìn)行重寫(xiě)的bgrewriteaofCommand 函數(shù)會(huì)檢測(cè)檢測(cè)是否滿足進(jìn)行重寫(xiě)的條件,主要檢測(cè)以下兩個(gè)條件:
【Condition1】:檢測(cè)當(dāng)前是否存在已經(jīng)在執(zhí)行的AOF重寫(xiě)子進(jìn)程,如果存在的話Redis將不再執(zhí)行AOF文件重寫(xiě)。
【Condition2】:檢測(cè)當(dāng)前是否存在已經(jīng)在創(chuàng)建RDB文件的子進(jìn)程,如果存在的話Redis將AOF文件重寫(xiě)任務(wù)置為待調(diào)度狀態(tài),后續(xù)如果滿足了重寫(xiě)條件,則繼續(xù)執(zhí)行AOF文件重寫(xiě)任務(wù)。
也就是說(shuō),Redis檢測(cè)到當(dāng)前既沒(méi)有AOF重寫(xiě)子進(jìn)程也沒(méi)有RDB文件創(chuàng)建子進(jìn)程,那么就可以進(jìn)行AOF文件重寫(xiě)。對(duì)應(yīng)源碼如下:
超出配置閾值
如果Redis實(shí)例開(kāi)啟了AOF配置,同時(shí)配置了auto-aof-rewrite-percentage以及auto-aof-rewrite-min-size,如果超出了閾值會(huì)觸發(fā)AOF重寫(xiě)。
aof_rewrite_scheduled被設(shè)置為待調(diào)度狀態(tài)
在bgrewriteaofCommand函數(shù)中,如果當(dāng)前正在執(zhí)行RDB dump操作,那么對(duì)應(yīng)的aof待調(diào)度aof_rewrite_scheduled狀態(tài)就會(huì)被置為1,當(dāng)前RDB dump完成之后,會(huì)繼續(xù)執(zhí)行AOF重寫(xiě)操作。?
AOF重寫(xiě)過(guò)程是怎樣的?
通過(guò)上文的描述,我們知道了Redis觸發(fā)AOF重寫(xiě)的時(shí)機(jī),那么當(dāng)觸發(fā)重寫(xiě)之后的具體業(yè)務(wù)是怎樣的呢?我們一起看下AOF重寫(xiě)的大致流程:
(1)Redis主進(jìn)程首先檢查是不是存在rdb dump進(jìn)程或者aof重寫(xiě)進(jìn)程正在運(yùn)行,如果不存在Redis主進(jìn)程fork子進(jìn)程進(jìn)行aof文件重寫(xiě);
(2)fork出來(lái)的子進(jìn)程和原來(lái)的Redis主進(jìn)程擁有同樣的內(nèi)存數(shù)據(jù),子進(jìn)程遍歷此時(shí)的內(nèi)存數(shù)據(jù)同時(shí)將內(nèi)存數(shù)據(jù)寫(xiě)入到臨時(shí)的AOF文件中;
(3)主進(jìn)程此時(shí)仍然可以接收客戶(hù)端請(qǐng)求,同時(shí)將新的緩存操作寫(xiě)入aof_buf以及aof_rewrite_buf中,根據(jù)對(duì)應(yīng)的同步策略,將buf中的數(shù)據(jù)分別寫(xiě)入舊AOF文件以及臨時(shí)AOF文件中;
(4)重寫(xiě)完成之后,臨時(shí)AOF文件將替換原有的老的AOF文件,從而完成整個(gè)AOF重寫(xiě)。
AOF模式優(yōu)點(diǎn)
1、AOF的持久化策略更加豐富些,可以根據(jù)實(shí)際業(yè)務(wù)需要進(jìn)行配置,因此相對(duì)來(lái)說(shuō)在數(shù)據(jù)可靠性方面要更加有優(yōu)勢(shì)一點(diǎn)。
2、AOF文件內(nèi)容比較好理解,更加方便理解業(yè)務(wù)緩存數(shù)據(jù)。
AOF模式缺點(diǎn)
1、通常情況下,同樣的緩存數(shù)據(jù),AOF文件比RDB文件大小要大一些。
2、在文件恢復(fù)場(chǎng)景下,AOF要比DRB恢復(fù)數(shù)據(jù)慢一些。
RDB持久化
RDB(Redis Data Base),所謂的Redis內(nèi)存數(shù)據(jù)快照就是某一時(shí)刻Redis存于內(nèi)存中的所有緩存數(shù)據(jù),這就好比用手機(jī)相機(jī)拍照,記錄當(dāng)時(shí)的美好畫(huà)面。Redis可以實(shí)現(xiàn)在固定時(shí)間間隔后將內(nèi)存中的緩存數(shù)據(jù)持久化保存起來(lái)。這樣即便是服務(wù)器宕機(jī)或者重啟了,只要RDB快照文件還存在,快照文件中對(duì)應(yīng)的緩存數(shù)據(jù)就不會(huì)丟失,Redis重新啟動(dòng)后會(huì)重新加載RDB文件到內(nèi)存中,快速恢復(fù)緩存數(shù)據(jù),通過(guò)這樣的方式保障了緩存數(shù)據(jù)的可靠性。
RDB文件生成過(guò)程
我們以bgsave為例子來(lái)看下Redis生成RDB文件的大致過(guò)程是怎樣的。
(1)Redis主進(jìn)程首先判斷當(dāng)前是否存在已經(jīng)在執(zhí)行的aof重寫(xiě)子進(jìn)程以及rdb文件生成子進(jìn)程,如果存在的話則直接進(jìn)行返回。為什么要進(jìn)行這樣的判斷呢?主要還是從服務(wù)器性能方面進(jìn)行考量,如果服務(wù)器有多個(gè)子線程在進(jìn)行RDB持久化操作,那么必定會(huì)對(duì)磁盤(pán)造成比較大的IO壓力,如果服務(wù)器中還部署了其他服務(wù)甚至?xí)绊懫渌?wù)的正常運(yùn)行。
(2)Redis主進(jìn)程fork子進(jìn)程進(jìn)行RDB文件生成操作,在fork的過(guò)程中,此時(shí)的Redis主進(jìn)程是阻塞的,不能響應(yīng)客戶(hù)端請(qǐng)求,子進(jìn)程fork完成之后可以繼續(xù)響應(yīng)客戶(hù)端請(qǐng)求。
(3)fork出來(lái)的子進(jìn)程遍歷內(nèi)存數(shù)據(jù)進(jìn)行RDB文件生成操作。
(4)如果此時(shí)客戶(hù)端的請(qǐng)求需要修改緩存數(shù)據(jù),那么如上面fork子進(jìn)程的原理,通過(guò)COW機(jī)制,操作系統(tǒng)會(huì)開(kāi)辟新的內(nèi)存空間給Redis主進(jìn)程進(jìn)行新的緩存數(shù)據(jù)寫(xiě)入。
(5)子進(jìn)程快照數(shù)據(jù)生成完成之后,替換原來(lái)老的RDB文件。
RDB觸發(fā)時(shí)機(jī)
Redis主要支持兩種持久化操作來(lái)生成RDB文件,分別是save、bsave命令方式手動(dòng)生成以及在配置文件中配置時(shí)間間隔自動(dòng)進(jìn)行RDB文件生成。
手動(dòng)命令觸發(fā)
客戶(hù)端連接到redis之后我們可以通過(guò)save以及bsave命令進(jìn)行RDB文件的立即創(chuàng)建,兩者的區(qū)別如下:
save:通過(guò)主線程觸發(fā),會(huì)阻塞Redis業(yè)務(wù),如果內(nèi)存數(shù)據(jù)比較多的話,會(huì)導(dǎo)致長(zhǎng)時(shí)間不能響應(yīng)外部請(qǐng)求;
bsave:客戶(hù)端執(zhí)行bsave命令進(jìn)行RDB持久化,Redis主線程會(huì)fork子線程出來(lái)進(jìn)行RDB文件持久化操作,這樣避免了主線程的阻塞即便正在持久化操作依然可以響應(yīng)外部數(shù)據(jù)緩存請(qǐng)求。
不過(guò)這里值得注意的是,雖然fork子進(jìn)程之后不會(huì)阻塞主進(jìn)程,但是在fork的過(guò)程中會(huì)阻塞主進(jìn)程,尤其是在內(nèi)存數(shù)據(jù)比較大的時(shí)候,阻塞主進(jìn)程的時(shí)間會(huì)更長(zhǎng)。
配置自動(dòng)觸發(fā)
另外在Redis的配置文件redis.conf中,我們可以配置按照一定的時(shí)間間隔來(lái)進(jìn)行RDB持久化操作。如下配置:
save 900 1
save 300 10
save 60 10000?
其他的觸發(fā)RDB文件生成的操作這里不再贅述了,像從節(jié)點(diǎn)執(zhí)行全量數(shù)據(jù)同步的時(shí)候,也會(huì)觸發(fā)主節(jié)點(diǎn)生成RDB文件發(fā)送給從節(jié)點(diǎn)。
RDB有沒(méi)有丟數(shù)據(jù)的風(fēng)險(xiǎn)?
大家不妨思考下通過(guò)RDB文件進(jìn)行緩存數(shù)據(jù)持久化會(huì)有什么問(wèn)題?存不存在丟失緩存數(shù)據(jù)的風(fēng)險(xiǎn)?這種方式看上去是個(gè)還不錯(cuò)的持久化解決方案,但是實(shí)際上隱藏著一些丟失緩存數(shù)據(jù)的風(fēng)險(xiǎn)。為什么這么說(shuō)呢?通過(guò)分析RDB文件生成的機(jī)制我們可以發(fā)現(xiàn)有兩個(gè)地方存在緩存數(shù)據(jù)丟失的可能性。
場(chǎng)景1:
由于Redis保存RDB快照文件的策略是按照配置的時(shí)間間隔進(jìn)行持久化保存,也就是每隔一個(gè)時(shí)間間隔Redis就會(huì)保存一個(gè)RDB文件。因此在內(nèi)存數(shù)據(jù)有更新但是RDB保存時(shí)間尚未到來(lái)的這段時(shí)間如果存在服務(wù)器宕機(jī)或者服務(wù)器重啟的情況,此時(shí)內(nèi)存的數(shù)據(jù)就會(huì)存在丟失的風(fēng)險(xiǎn),因?yàn)镽edis還沒(méi)來(lái)得及將數(shù)據(jù)持久化到RDB文件中。
場(chǎng)景1中最大的問(wèn)題就RDB文件持久化存在時(shí)間間隔,而這個(gè)時(shí)間間隔導(dǎo)致了新增的緩存數(shù)據(jù)存在丟失的風(fēng)險(xiǎn)。那么是不是將時(shí)間間隔降低到最小就可以了,比如一秒鐘,即使在這一秒鐘期間出現(xiàn)異常情況,那緩存數(shù)據(jù)也只是丟掉這一秒鐘的緩存數(shù)據(jù),相對(duì)來(lái)說(shuō)數(shù)據(jù)丟失的情況可控一點(diǎn)。但是問(wèn)題是如果真的每隔1s就保存一個(gè)RDB文件到服務(wù)器磁盤(pán)中,那不論是對(duì)Redis本身還是Redis所在的服務(wù)器磁盤(pán)IO都是一種負(fù)擔(dān)。
場(chǎng)景2:
?隨著業(yè)務(wù)的不斷發(fā)展,內(nèi)存中的數(shù)據(jù)必定會(huì)越來(lái)越大,因此在fork子進(jìn)程來(lái)生成RDB文件的過(guò)程中,需要復(fù)制的數(shù)據(jù)會(huì)同樣越來(lái)越多,耗費(fèi)的時(shí)間也會(huì)越來(lái)越多,進(jìn)而阻塞主進(jìn)程的時(shí)間也會(huì)越來(lái)越多。如果出現(xiàn)長(zhǎng)時(shí)間阻塞主進(jìn)程的情況,那么Redis實(shí)例必定無(wú)法響應(yīng)客戶(hù)端的數(shù)據(jù)操作請(qǐng)求,最終導(dǎo)致內(nèi)存數(shù)據(jù)沒(méi)有進(jìn)行及時(shí)更新,從而出現(xiàn)丟失緩存數(shù)據(jù)的風(fēng)險(xiǎn)。
RDB模式優(yōu)點(diǎn)
1、相比AOF在恢復(fù)數(shù)據(jù)的時(shí)候需要一條條回放操作命令,通過(guò)RDB文件恢復(fù)數(shù)據(jù)效率更高;
2、適合全量備份內(nèi)存數(shù)據(jù)場(chǎng)景。
3、同樣規(guī)模的內(nèi)存數(shù)據(jù),RDB文件數(shù)據(jù)更加緊湊,磁盤(pán)空間占用更小。
4、可以根據(jù)不同的時(shí)間間隔保存RDB文件,在恢復(fù)數(shù)據(jù)的時(shí)候可以更加靈活地選擇對(duì)應(yīng)版本數(shù)據(jù)進(jìn)行恢復(fù)。
RDB模式缺點(diǎn)
1、由于RDB數(shù)據(jù)保存存在一定的時(shí)間間隔,因此存在丟失緩存數(shù)據(jù)的風(fēng)險(xiǎn);
2、fork子進(jìn)程進(jìn)行RDB文件生成,由于是一次性生成一個(gè)內(nèi)存快照文件,對(duì)于服務(wù)器磁盤(pán)IO以及Redis本身來(lái)說(shuō)都屬于重操作,可能會(huì)對(duì)服務(wù)器的磁盤(pán)IO造成壓力。
混合持久化
既然AOF以及RDB持久化都有這樣或者那樣的不足,那么有沒(méi)有一種持久化方案可以兼顧二者的優(yōu)點(diǎn)來(lái)?yè)P(yáng)長(zhǎng)避短呢?從4.0版本開(kāi)始,Redis支持混合持久化的方式來(lái)兼顧效率以及數(shù)據(jù)可靠性。在Redis配置文件redis.conf中配置混合持久化:
如果配置了混合持久化,那么Redis主進(jìn)程在fork子進(jìn)程進(jìn)行持久化操作的時(shí)候,原先的將內(nèi)存數(shù)據(jù)轉(zhuǎn)換為操作命令的過(guò)程將替換為使用進(jìn)行AOF重寫(xiě)時(shí)對(duì)應(yīng)的RDB文件內(nèi)容直接放入到重寫(xiě)后的臨時(shí)文件中,后面再有新的操作命令,都追加到臨時(shí)aof文件中,重寫(xiě)完成后使用臨時(shí)aof文件替換舊的文件。
混合持久化模式優(yōu)點(diǎn)
1、同時(shí)擁有RDB以及AOF機(jī)制的優(yōu)點(diǎn),在數(shù)據(jù)可靠性以及數(shù)據(jù)恢復(fù)效率上面達(dá)到了很好的平衡。?
混合持久化模式缺點(diǎn)
1、由于Redis從4.0版本才開(kāi)始支持混合持久化,如果當(dāng)前平臺(tái)中的Redis版本低于4.0,那么就無(wú)法使用這個(gè)持久化機(jī)制,因此兼容性不夠友好;
總結(jié)
本文主要分析了Redis AOF、RDB以及混合持久化的內(nèi)存數(shù)據(jù)持久化的機(jī)制原理,同時(shí)分析了兩種持久化方式的優(yōu)點(diǎn)以及缺點(diǎn)。我想只有理解了中間件的特性機(jī)制原理,知道了特性的長(zhǎng)處以及不足我們才能設(shè)計(jì)適合我們平臺(tái)的緩存數(shù)據(jù)持久化策略,從而提升平臺(tái)的穩(wěn)定性。
另外在一些優(yōu)秀中間件的學(xué)習(xí)和使用過(guò)程中,我們不能僅僅停留在會(huì)用的層面,更應(yīng)該深入底層領(lǐng)會(huì)其架構(gòu)和實(shí)現(xiàn)機(jī)制的設(shè)計(jì)思路,只有搞明白設(shè)計(jì)思路,時(shí)刻站在設(shè)計(jì)者的角度來(lái)看待遇到的問(wèn)題,那么在我們的實(shí)際工作中,如果遇到類(lèi)似的問(wèn)題我們可以借鑒這些優(yōu)秀中間件的解決思路來(lái)進(jìn)行問(wèn)題分析。