一杯茶的功夫,上手Redis持久化機(jī)制
本文轉(zhuǎn)載自微信公眾號(hào)「石杉的架構(gòu)筆記」,作者崔皓 。轉(zhuǎn)載本文請(qǐng)聯(lián)系石杉的架構(gòu)筆記公眾號(hào)。
開(kāi)篇
Redis作為最常用的內(nèi)存數(shù)據(jù)庫(kù),通常來(lái)說(shuō)數(shù)據(jù)存儲(chǔ)在內(nèi)存中,為了避免Redis服務(wù)器進(jìn)程退出導(dǎo)致內(nèi)存中的數(shù)據(jù)消失。Redis提出了持久化機(jī)制,也就是把內(nèi)存中的數(shù)據(jù)保存到磁盤(pán)中,從而提高數(shù)據(jù)存儲(chǔ)的可靠性。為此主流數(shù)據(jù)庫(kù)會(huì)提供兩類持久化方案,它們是“快照”存儲(chǔ)和“日志”存儲(chǔ)。相應(yīng)地Redis提供了RDB持久化和AOF持久化與之對(duì)應(yīng)。其中RDB是以快照的方式存儲(chǔ)內(nèi)存數(shù)據(jù)到磁盤(pán)上,而AOF是以日志追加的方式進(jìn)行存儲(chǔ)。下面就圍繞這兩種持久化方式展開(kāi)如下內(nèi)容:
- RDB 文件結(jié)構(gòu)
- RDB觸發(fā)機(jī)制以及流程
- AOF持久化流程
- AOF緩沖區(qū)同步文件策略
- AOF重寫(xiě)
- RDB和AOF的比較
RDB持久化
RDB是Redis Database 的縮寫(xiě),其作用是在某一個(gè)時(shí)間點(diǎn),將Redis存儲(chǔ)在內(nèi)存中的數(shù)據(jù)生成快照并存儲(chǔ)到磁盤(pán)等介質(zhì)上,存在這個(gè)磁盤(pán)介質(zhì)上的文件就是RDB文件。“快照”顧名思義就是好像照相一樣保存當(dāng)時(shí)的數(shù)據(jù),這里的RDB文件是一個(gè)二進(jìn)制的文件,并且是經(jīng)過(guò)壓縮的。因?yàn)镽DB文件是保存在硬盤(pán)中的,即使Redis服務(wù)器進(jìn)程退出,甚至運(yùn)行Redis服務(wù)器的計(jì)算機(jī)宕機(jī),但只要RDB文件仍然存在,Redis服務(wù)器就可以用它來(lái)還原數(shù)據(jù)庫(kù)狀態(tài)。如圖1 所示,可以想象Redis數(shù)據(jù)庫(kù)在時(shí)間軸上有位于不同時(shí)間點(diǎn)的時(shí)候都有一個(gè)數(shù)據(jù)庫(kù)狀態(tài),可以把它們想象成一個(gè)個(gè)切片。圖上標(biāo)注出兩個(gè)時(shí)間點(diǎn)的兩個(gè)數(shù)據(jù)庫(kù)切片,RDB持久化做的事情就是順著綠色箭頭的方向?qū)?shù)據(jù)庫(kù)狀態(tài)的“切片”以RDB文件的形式保存到磁盤(pán)中。
圖1 將數(shù)據(jù)庫(kù)狀態(tài)保存為RDB文件
RDB 文件結(jié)構(gòu)
雖然說(shuō)RDB是一個(gè)壓縮過(guò)的二進(jìn)制文件,但是它的文件結(jié)構(gòu)也需要有基本的了解,這樣有助于我們理解其發(fā)揮的作用。如表格1所示,RDB文件由5個(gè)部分組成,按照從左到右的順序依次是:
- 文件最開(kāi)頭是“REDIS”部分,其長(zhǎng)度為5個(gè)字節(jié),保存著“REDIS”五個(gè)字符。通過(guò)這五個(gè)字符,程序可以在載入文件時(shí)可以判斷所載入的文件是否是RDB文件。
- 接下來(lái)是“db_version”長(zhǎng)度為4字節(jié),是一個(gè)字符串表示的整數(shù),它記錄了RDB文件的版本號(hào),例如:"0006"就代表RDB文件的版本為第六版。
- “databases”中可以包含著零個(gè)或任意多個(gè)數(shù)據(jù)庫(kù)。
- “EOF”常量的長(zhǎng)度為1字節(jié),是 RDB文件正文結(jié)束的標(biāo)識(shí),當(dāng)載入程序讀取到個(gè)值的時(shí),就意味著數(shù)據(jù)庫(kù)的所有鍵值對(duì)都已經(jīng)加載完畢了。
- “check_sum”是一個(gè)8字節(jié)長(zhǎng)的無(wú)符號(hào)整數(shù),保存著一個(gè)校驗(yàn)和。這個(gè)校驗(yàn)和是通過(guò)對(duì)“REDIS”、“db_version”、“databases”、以及“EOF”四個(gè)部分的內(nèi)容進(jìn)行計(jì)算得出的。Redis服務(wù)器在載入RDB文件時(shí),會(huì)將載入數(shù)據(jù)所計(jì)算出的校驗(yàn)和與check_sum所記錄的校驗(yàn)和進(jìn)行對(duì)比,以此來(lái)判斷RDB文件是否損壞。
db_version |
databases |
EOF |
表格1 RDB的文件結(jié)構(gòu)
如表格2所示,其表示一個(gè)databases部分為空的RDB文件:文件以"REDIS"開(kāi)頭,表示這是一個(gè)RDB文件,之后的"0006"表示數(shù)據(jù)庫(kù)版本是第六版。因?yàn)閐atabases為空所以這里沒(méi)有數(shù)據(jù)庫(kù)的信息,所以版本號(hào)之后直接跟著“EOF”常量,最后的6265312314761934562是文件的校驗(yàn)和。
“0006” |
EOF |
6265312314761934562 |
表格2 RDB文件例子
RDB 觸發(fā)機(jī)制以及流程
在認(rèn)識(shí)了RDB文件的結(jié)構(gòu)以后,再來(lái)看看Redis 如何觸發(fā)RDB持久化操作,以及其具體流程是如何的。這里包括三部分的內(nèi)容,分別是save同步方式觸發(fā)、bgsave 異步方式觸發(fā)以及自動(dòng)配置化的方式觸發(fā)。
save同步方式觸發(fā)RDB持久化
如圖2所示,描述了save 同步方式持久化RDB的過(guò)程:
- Redis Client端通過(guò)向Redis Server 發(fā)起save命令請(qǐng)求RDB持久化操作。
- Redis Server接受到命令以后,將當(dāng)前數(shù)據(jù)庫(kù)快照保存到RDB文件中。
- 由于save命令是同步操作,因此如果此時(shí)有其他Redis Client也向Redis Server發(fā)起save操作,會(huì)被阻塞直到第一個(gè)Redis Client完成save命令為止。
圖2 save同步方式觸發(fā)RDB持久化
bgsave同步方式觸發(fā)RDB持久化
如圖3所示,異步方式過(guò)程如下:
- 依舊是Redis Client發(fā)起命令,不過(guò)命令改成了bgsave(background save有后臺(tái)運(yùn)行的意思),照舊請(qǐng)求Redis Server。
- Redis Server接受到請(qǐng)求以后會(huì)fork出一個(gè)Redis子進(jìn)程。
- 這個(gè)子進(jìn)程用來(lái)創(chuàng)建RDB文件。由于這個(gè)過(guò)程是異步的,因此Redis Server在啟動(dòng)子進(jìn)程以后還可以接受其他請(qǐng)求。
- Redis 子進(jìn)程創(chuàng)建RDB文件以后會(huì)把成功的消息返回給Redis Server。
- 由于bgsave命令是異步操作,如果此時(shí)有其他Redis Client同時(shí)請(qǐng)求Redis Server并不會(huì)被阻塞。Redis Server會(huì)響應(yīng)請(qǐng)求,同樣也會(huì)fork出對(duì)應(yīng)的子進(jìn)程進(jìn)行RDB文件的創(chuàng)建。
圖3 bgsave異步方式觸發(fā)RDB持久化
自動(dòng)配置化的方式觸發(fā)
如圖4所示,這種方式可以理解為讀取配置文件的方式。看下面的三個(gè)步驟:
- Redis Server直接讀取Redis 配置文件的內(nèi)容,獲取RDB持久化的信息。
- 在Redis配置文件中配置了對(duì)應(yīng)的save命令用來(lái)代替Redis Client請(qǐng)求的命令。其配置內(nèi)容包括save命令、秒和修改次數(shù)。按照?qǐng)D中的例子來(lái)說(shuō),“save 500 3” 的意思是,在500秒的時(shí)間內(nèi)如果Redis 數(shù)據(jù)庫(kù)有3次修改就進(jìn)行save請(qǐng)求,也就是請(qǐng)求RDB的持久化操作。
- 一旦滿足配置文件中的條件,Redis Server就會(huì)執(zhí)行對(duì)應(yīng)的save操作進(jìn)行持久化。
圖4 讀取配置文件進(jìn)行RDB持久化
一般而言Redis的配置信息會(huì)放到redis.conf配置文件中進(jìn)行存儲(chǔ),其包含很多內(nèi)容,這里我們就RDB持久化的部分給大家做簡(jiǎn)單介紹。redis.conf文件中找到“SNAPSHOTTING”(快照)的部分??慈缦聨讉€(gè)配置項(xiàng):
- # 900秒內(nèi)有1次修改、300秒內(nèi)有10次修改、60秒內(nèi)有10000次修改
- #滿足任何以上條件,觸發(fā)RDB持久化。
- save 900 1
- save 300 10
- save 60 10000
- # 當(dāng)快照操作bgsave出錯(cuò)時(shí),是否停止持久化?yes 表示“是”,no表示“否”。
- stop-writes-on-bgsave-error yes
- # 是否壓縮?yes表示“是”,no表示“否”,默認(rèn)選擇yes。
- rdbcompression yes
- # 對(duì)rdb數(shù)據(jù)進(jìn)行校驗(yàn), 表示寫(xiě)入文件和讀取文件時(shí)是否開(kāi)啟 RDB 文件檢查。
- # yes表示“是”,no表示“否”,默認(rèn)選擇yes。
- # 選擇yes表示在Redis加載RDB需要檢查文件是否損壞,如果存在損壞會(huì)停止啟動(dòng)。
- rdbchecksum yes
- # 設(shè)置rdb的文件名
- dbfilename dump.rdb
- # RDB文件的路徑,如果不單獨(dú)指定,默認(rèn)是redis的啟動(dòng)路徑。
- dir ./
關(guān)于RDB持久化恢復(fù)Redis數(shù)據(jù)方面也比較簡(jiǎn)單,將RDB持久化文件 (例如:dump.rdb) 移動(dòng)到 Redis 安裝目錄并啟動(dòng)Redis服務(wù)就可以了??梢酝ㄟ^(guò) Redis 中的“CONFIG GET dir”命令獲取Redis的安裝目錄。
由于RDB是一個(gè)壓縮的二進(jìn)制文件,其代表Redis在某一個(gè)時(shí)間點(diǎn)上的快照。其適合數(shù)據(jù)庫(kù)備份和全量復(fù)制的場(chǎng)景。比如定期給數(shù)據(jù)庫(kù)進(jìn)行備份,把RDB文件拷貝到其他的服務(wù)器上,以及用于災(zāi)備。同樣是因?yàn)閴嚎s的原因,RDB的加載速度比AOF也要快。
AOF持久化
上面介紹了RDB的執(zhí)行方式和流程,這種方式?jīng)]有辦法做到實(shí)時(shí)持久化的要求。因?yàn)闊o(wú)論是save還是bgsave每次運(yùn)行都要消耗大量的資源(CPU、內(nèi)存、磁盤(pán))。隨著數(shù)據(jù)庫(kù)本身容量的增加每次備份的數(shù)據(jù)量也隨之增加。同時(shí)RDB是二進(jìn)制保存,當(dāng)Redis版本演進(jìn)過(guò)程中有多個(gè)格式的RDB版本,會(huì)存在老版本RDB與新版本格式兼容的問(wèn)題。正式因?yàn)镽DB的這些問(wèn)題,Redis提出了AOF的持久化方式。AOF(append only file),是以日志的方式記錄每次寫(xiě)入的命令,在Redis Server啟動(dòng)的時(shí)候會(huì)重新執(zhí)行AOF文件中的命令,從而達(dá)到恢復(fù)數(shù)據(jù)的目的。AOF可以解決數(shù)據(jù)持久化的實(shí)時(shí)性問(wèn)題,也是當(dāng)前Redis主流的持久化方式。
AOF持久化流程
上面提到了AOF持久化的過(guò)程就是日志不斷追加的過(guò)程,這里通過(guò)圖5 給大家介紹具體流程:
- Redis Client作為命令的來(lái)源,會(huì)有多個(gè)源頭以及源源不斷的請(qǐng)求命令。
- 在這些命令到達(dá)Redis Server 以后,并不是直接寫(xiě)入AOF文件,會(huì)將其這些命令先放入AOF緩存中進(jìn)行保存。這里的AOF緩沖區(qū)實(shí)際上是內(nèi)存中的一片區(qū)域,存在的目的是當(dāng)這些命令達(dá)到一定量以后再寫(xiě)入磁盤(pán),避免頻繁的磁盤(pán)IO操作。
- AOF緩沖會(huì)根據(jù)對(duì)應(yīng)的策略將命令寫(xiě)入磁盤(pán)上的AOF文件。
- AOF文件隨著寫(xiě)入文件內(nèi)容的增加,會(huì)根據(jù)規(guī)則進(jìn)行命令的合并,這里叫做AOF重寫(xiě),從而起到AOF文件壓縮的目的。
- 當(dāng)Redis Server 服務(wù)器重啟的時(shí)候會(huì)從AOF文件載入數(shù)據(jù)。
圖5 AOF 處理流程圖
AOF緩沖區(qū)同步文件策略
上面提到了Redis 會(huì)將命令先寫(xiě)入到AOF緩沖區(qū),再寫(xiě)入AOF文件。這里介紹一下AOF緩沖區(qū)同步文件的三個(gè)策略。
- always 策略:命令寫(xiě)入AOF緩沖區(qū)以后會(huì)調(diào)用系統(tǒng)fsync 操作同步到AOF文件,fsync完成后線程返回。這里的fsync是針對(duì)單個(gè)文件的操作,在進(jìn)行磁盤(pán)同步的時(shí)候會(huì)阻塞直到寫(xiě)入磁盤(pán)完成以后返回,從而保證數(shù)據(jù)持久化的完成。
- everysec 策略:命令寫(xiě)入AOF緩沖區(qū)以后調(diào)用write操作,write完成后線程返回。此操作會(huì)有專門線程執(zhí)行每秒執(zhí)行一次。這里的write操作會(huì)觸發(fā)延遲寫(xiě)(delayed write)機(jī)制,Linux 內(nèi)核提供頁(yè)緩沖區(qū)來(lái)提高硬盤(pán)IO性能。也就是說(shuō)write 操作寫(xiě)入系統(tǒng)緩沖區(qū)以后就返回了,同步硬盤(pán)依賴于操作系統(tǒng)調(diào)度機(jī)制完成。(Redis默認(rèn)配置)
- no策略:此種刷新策略是根據(jù)操作系統(tǒng)來(lái)決定的,也就是由操作系統(tǒng)來(lái)決定什么時(shí)候?qū)⒕彌_區(qū)的數(shù)據(jù)寫(xiě)入到磁盤(pán)中。由于是操作系統(tǒng)來(lái)決定持久化,所以這種方式是不可控的。
AOF 重寫(xiě)
AOF緩沖區(qū)會(huì)將Redis Client請(qǐng)求的命令源源不斷地同步到AOF文件中,同時(shí)AOF文件會(huì)不斷增大,這里就需要AOF重寫(xiě)。AOF重寫(xiě)就是把Redis進(jìn)程內(nèi)的數(shù)據(jù)轉(zhuǎn)化為寫(xiě)命令同步到新的AOF文件的過(guò)程。其目的就是使重寫(xiě)后的AOF文件變得更小:
- 進(jìn)程內(nèi)已經(jīng)超時(shí)的數(shù)據(jù)不會(huì)再寫(xiě)入AOF文件中。
- 舊AOF文件含有的無(wú)效命令,可以通過(guò)進(jìn)程內(nèi)的數(shù)據(jù)直接生成,新的AOF文件只保留最終的數(shù)據(jù)寫(xiě)入命令。例如就文件中存在三條命令,它們依次是“set hello A”、 “set hello B”和“set hello C”,對(duì)同一個(gè)key 進(jìn)行負(fù)值只有最后一句“set hello C”是起效的,所以這三條命令會(huì)被“set hello C”一條命令替換,并且保存到新的AOF文件中。
- 另外,多條寫(xiě)命令可以合并成一個(gè)。例如依次存在三個(gè)命令:“lpush list A”、 “lpush list B”和“lpush list C”,這里就可以合并為一條命令“lpush list A B C”。
AOF重寫(xiě)不僅降低了文件的占用空間,同時(shí)更小的AOF也可以更快地被Redis加載。
說(shuō)完了AOF重寫(xiě)的定義以后,下面來(lái)看看AOF重寫(xiě)的流程。一般而言有兩種方式可以執(zhí)行重寫(xiě)操作,分別是:bgrewriteaof 命令和AOF重寫(xiě)配置。
bgrewriteaof命令重寫(xiě)
如圖6 所示,整個(gè)執(zhí)行過(guò)程由三步組成:
- Redis Client發(fā)起bgrewriteaof命令,這個(gè)命令是一個(gè)異步命令。由于Redis Server 在接受bgrewriteaof命令的同時(shí),還可以接受其他Redis Client的命令,因此后面的第二步和第三步實(shí)際上是并行進(jìn)行的。第二步:進(jìn)行AOF重寫(xiě),在同時(shí)第三步:還會(huì)接受其他非重寫(xiě)的命令請(qǐng)求。
- Redis Server接受到這個(gè)命令以后,會(huì)啟動(dòng)一個(gè)Redis的子進(jìn)程用來(lái)執(zhí)行AOF重寫(xiě)操作。這個(gè)重寫(xiě)過(guò)程實(shí)際上是針對(duì)Redis內(nèi)存中的數(shù)據(jù)進(jìn)行回溯,也就是下方紅色區(qū)域的“AOF重寫(xiě)緩沖區(qū)”。
- 正如在第一步中提到的,在進(jìn)行第二步的同時(shí)第三步還在接受客戶端的請(qǐng)求并且通過(guò)“AOF緩沖區(qū)”保存到“舊AOF文件”中。
- 最終,完成AOF重寫(xiě)操作以后將“新AOF文件”寫(xiě)入到“舊AOF文件”中完成AOF重寫(xiě)。
圖6 AOF重寫(xiě)流程圖
AOF配置重寫(xiě)
實(shí)際上是通過(guò)AOF的配置文件中的配置值來(lái)確定重寫(xiě)的時(shí)機(jī)。配置如下:
通過(guò)上面的配置可以得到AOF重寫(xiě)的機(jī)制如下:
- 當(dāng)AOF文件當(dāng)前尺寸大于AOF重寫(xiě)的最小尺寸的時(shí)候就觸發(fā)重寫(xiě)機(jī)制。通過(guò)上面配置來(lái)形成表達(dá)式就是:aof-current-size> auto-aof-rewrite-min-size
- 當(dāng)AOF文件當(dāng)前尺寸減去AOF文件本身尺寸的值除以AOF文件本身的尺寸得到的結(jié)果大于AOF文件重寫(xiě)比率的時(shí)候就需要出發(fā)重寫(xiě)機(jī)制。
表達(dá)式就是:aof-current-size- aof-base-size/ aof-base-size > auto-aof-rewrite-percentage
由于Redis的配置文件中RDB是默認(rèn)配置。AOF需要手動(dòng)開(kāi)啟。
需要到Redis的配置文件redis.conf中進(jìn)行如下設(shè)置。
- #開(kāi)啟AOF模式 ,yes表示“開(kāi)啟AOF模式”。
- appendonly yes
與RDB持久化文件恢復(fù)數(shù)據(jù)一樣,只需要將AOF文件 移動(dòng)到 Redis 安裝目錄,并啟動(dòng)Redis服務(wù)就可以在Redis啟動(dòng)的時(shí)候加載AOF文件恢復(fù)數(shù)據(jù)了。
通過(guò)上面對(duì)AOF的描述可以看出,AOF具有數(shù)據(jù)完整,安全性高(秒級(jí)數(shù)據(jù)丟失)等優(yōu)點(diǎn)。同時(shí)AOF文件以追加日志文件的形式存在,且寫(xiě)入操作是以Redis協(xié)議格式保存的,因此其內(nèi)容是可讀性強(qiáng),適合誤刪時(shí)的緊急恢復(fù)。不過(guò),相對(duì)于RDB而言其文件尺寸較大,會(huì)占用更多Redis啟動(dòng)加載數(shù)據(jù)的時(shí)間。
RDB和AOF的比較
前面介紹了RDB和AOF兩種機(jī)制,這里將它們做一個(gè)簡(jiǎn)單對(duì)比。
- 啟動(dòng)優(yōu)先級(jí):假設(shè)Redis 同時(shí)打開(kāi)了RDB和AOF持久化功能,當(dāng)Redis重啟的時(shí)候會(huì)優(yōu)先加載AOF,因?yàn)锳OF數(shù)據(jù)更新的頻率更高,會(huì)保存更新的數(shù)據(jù)。
- 體積大小/恢復(fù)速度:RDB是使用二進(jìn)制壓縮的模式保存,因此體積會(huì)比較小,在Redis恢復(fù)的時(shí)候加載的速度也會(huì)更快。相反AOF寫(xiě)入的是日志的形式,因此體積會(huì)較大,恢復(fù)速度也會(huì)慢些。
- 數(shù)據(jù)安全性:RDB是以快照的模式保存數(shù)據(jù),對(duì)數(shù)據(jù)的保存不是實(shí)時(shí)性的,會(huì)有丟失數(shù)據(jù)的可能性。而在這方面AOF的日志方式數(shù)據(jù)丟失的幾率會(huì)比RDB好很多(例如:everysec)。
- 資源消耗:RDB顯示需要消耗的資源會(huì)更大,因?yàn)槊看螌⑷康臄?shù)據(jù)保存到磁盤(pán)中。而AOF每次可以保存增量的Redis數(shù)據(jù)。
總結(jié)
本文從為什么需要數(shù)據(jù)庫(kù)持久化作為切入點(diǎn),談到Redis中的兩類數(shù)據(jù)庫(kù)持久化機(jī)制:RDB和AOF。其中針對(duì)RDB持久化通過(guò)介紹RDB文件結(jié)構(gòu)、觸發(fā)持久化的機(jī)制和流程進(jìn)行了闡述。其包括:save同步方式、bgsave同步方式和自動(dòng)配置方式。針對(duì)AOF持久化,通過(guò)AOF持久化流程、緩沖區(qū)同步文件策略以及AOF重寫(xiě)機(jī)制進(jìn)行了介紹。其中緩沖區(qū)同步策略包括:always策略、everysec策略和no策略。