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