redis(二:日志)

持久化對于任何數據庫來說都是重要的知識點。很久前寫過mongo和mysql的日志。今天記錄下redis的日志設計。

Redis把后端數據庫中的數據存儲在內存中,然后直接從內存中讀取數據,響應速度會非常快。但服務器宕機時,內存中的數據將全部丟失。如果從后端數據庫(比如:mysql)恢復這些數據,那會出現大的緩存穿透,會給數據庫帶來巨大的壓力;而且從慢速數據庫中讀取性能肯定比不上從 Redis 中讀取,導致使用程序響應變慢。所以,對 Redis 來說,實現數據的持久化很重要。
目前,Redis 的持久化主要有兩大機制,即 AOF(Append Only File)日志和 RDB 快照。

寫后日志:AOF
Redis 是先執行命令,把數據寫入內存,然后才記錄日志,如下圖所示:


傳統數據庫的日志,例如 redo log(重做日志),記錄的是修改后的數據,而 AOF 里記錄的是 Redis 收到的每一條命令,這些命令是以文本形式保存的。
我們以 Redis 收到“set testkey testvalue”命令后記錄的日志為例,看看 AOF 日志的內容。其中,“*3”表示當前命令有三個部分,每部分都是由“$+數字”開頭,后面緊跟著具體的命令、鍵或值。這里,“數字”表示這部分中的命令、鍵或值一共有多少字節。例如,“$3 set”表示這部分有 3 個字節,也就是“set”命令。

寫后日志的好處:
1,為了避免額外的檢查開銷,Redis 在向 AOF 里面記錄日志的時候,并不會先去對這些命令進行語法檢查。寫后日志這種方式保證了寫入日志的命令都是合法的。
2,是在命令執行后才記錄日志,所以不會阻塞當前的寫操作。

AOF落盤策略
Always,同步寫回:每個寫命令執行完,立馬同步地將日志寫回磁盤;
Everysec,每秒寫回:每個寫命令執行完,只是先把日志寫到 AOF 文件的內存緩沖區,每隔一秒把緩沖區中的內容寫入磁盤;
No,操作系統控制的寫回:每個寫命令執行完,只是先把日志寫到 AOF 文件的內存緩沖區,由操作系統決定何時將緩沖區內容寫回磁盤。

write 和 fsync
write 只要把日志記錄寫到內核緩沖區,就可以返回了,并不需要等待日志實際寫回到磁盤;
fsync 需要把日志記錄寫回到磁盤后才能返回,時間較長。


當寫回策略配置為 everysec 時,Redis 會使用后臺的子線程異步完成 fsync 的操作。
always 策略并不使用后臺子線程來執行。

落盤策略性能比較
Always可以做到基本不丟數據(執行瞬間立刻宕機,還未落盤還是會丟失數據),缺點:每一個寫命令后都有一個慢速的落盤操作,不可避免地會影響主線程性能;

AOF 重寫機制

AOF 是以文件的形式在記錄接收到的所有寫命令。隨著時間推移,AOF 文件會不斷膨脹。需要注意 AOF 文件過大帶來的性能問題。(如果發生宕機,AOF 中記錄的命令要一個個被重新執行,用于故障恢復,如果日志文件太大,整個恢復過程就會非常緩慢),這時候就需要用到AOF 重寫機制。

重寫機制通過“多變一”(舊日志文件中的多條命令,在重寫后的新日志中變成了一條命令)的方法,縮小 AOF 文件。


什么時候會觸發AOF 重寫?
有兩個配置項在控制AOF重寫的觸發時機:
1, auto-aof-rewrite-min-size: 表示運行AOF重寫時文件的最小大小,默認為64MB
2, auto-aof-rewrite-percentage: 這個值的計算方法是:當前AOF文件大小和上一次重寫后AOF文件大小的差值,再除以上一次重寫后AOF文件大小。也就是當前AOF文件比上一次重寫后AOF文件的增量大小,和上一次重寫后AOF文件大小的比值。
AOF文件大小同時超出上面這兩個配置項時,會觸發AOF重寫。

AOF重寫對主線程的影響

和 AOF日志落盤不同,重寫過程是由后臺子進程 bgrewriteaof 來完成的。這個過程并不會阻塞主線程。

重寫的過程總結為“一個拷貝,兩處日志”。
一個拷貝
每次執行重寫時,主線程 fork 出后臺的 bgrewriteaof 子進程。此時,fork 會把主線程的內存映射拷貝一份給 bgrewriteaof 子進程,這里面就包含了數據庫的最新數據。然后,bgrewriteaof 子進程就可以在不影響主線程的情況下,逐一把拷貝的數據寫成操作,記入重寫日志。
兩處日志
舊的AOF日志:因為主線程未阻塞,仍然可以處理新來的操作。Redis 會把這個操作寫到它的緩沖區(很快就合到AOF日志中,需要看落盤策略)。這樣一來,即使宕機了,這個 AOF 日志的操作仍然是齊全的,可以用于恢復。
新的AOF日志:AOF 重寫日志。這個操作也會被寫到重寫日志的緩沖區。(拷貝完才合到日志中)這樣,重寫日志也不會丟失最新的操作。等到拷貝數據的所有操作記錄重寫完成后,重寫日志記錄的這些最新操作也會寫入新的 AOF 文件,以保證數據庫最新狀態的記錄。此時,我們就可以用新的 AOF 文件替代舊文件了。

aof緩沖區:是正常使用aof作為數據落地中間地帶,所有的數據先到aof緩沖區再到aof文件中。
aof重寫緩沖區: 是aof重寫時,redis還要繼續接收數據,這個數據就寫到aof重寫緩沖區,當aof重寫ok時,主進程在把aof重寫緩沖區的數據寫到aof緩沖區,最后fsync到aof文件中。

AOF重寫的時候,子線程會首先拷貝必要的數據結構包括內存頁表,完成了這個操作就可以進行重寫,只不過父子進程這個時候指向的是同一個內存,在子進程重寫過程中若父進程操作了已有的key,則會重新申請新的內存,這樣父子進程就逐漸的擁有獨自的內存空間。
總結Linux的Copy On Write技術:
1,fork出的子進程共享父進程的物理空間,當父子進程有內存寫入操作時,read-only內存頁發生中斷,將觸發的異常的內存頁復制一份(其余的頁還是共享父進程的)。
2,fork出的子進程功能實現和父進程是一樣的。如果有需要,我們會用exec()把當前進程映像替換成新的進程文件,完成自己想要實現的功能。
Copy On Write機制了解一下

簡單來說就是重寫是復制一份地址映射,父線程只要改動了,子線程就開辟新的空間,映射修改。然后還記錄一份緩沖區緩存日志,等備份完再執行一下緩沖區的日志。

Redis采用fork子進程重寫AOF文件時,潛在的阻塞風險包括:fork子進程 和 AOF重寫過程中父進程產生寫入的場景
fork子進程,fork這個瞬間一定是會阻塞主線程的,fork采用操作系統提供的寫實復制(Copy On Write)機制,就是為了避免一次性拷貝大量內存數據給子進程造成的長時間阻塞問題,但fork子進程需要拷貝進程必要的數據結構,其中有一項就是拷貝內存頁表(虛擬內存和物理內存的映射索引表),這個拷貝過程會消耗大量CPU資源,拷貝完成之前整個進程是會阻塞的,阻塞時間取決于整個實例的內存大小,實例越大,內存頁表越大,fork阻塞時間越久。
拷貝內存頁表完成后,子進程與父進程指向相同的內存地址空間,也就是說此時雖然產生了子進程,但是并沒有申請與父進程相同的內存大小。那什么時候父子進程才會真正內存分離呢?“寫實復制”顧名思義,就是在寫發生時,才真正拷貝內存真正的數據,這個過程中,父進程也可能會產生阻塞的風險,就是下面介紹的場景。

fork出的子進程指向與父進程相同的內存地址空間,此時子進程就可以執行AOF重寫,把內存中的所有數據寫入到AOF文件中。但是此時父進程依舊是會有流量寫入的,如果父進程操作的是一個已經存在的key,那么這個時候父進程就會真正拷貝這個key對應的內存數據,申請新的內存空間,這樣逐漸地,父子進程內存數據開始分離,父子進程逐漸擁有各自獨立的內存空間。因為內存分配是以頁為單位進行分配的,默認4k,如果父進程此時操作的是一個bigkey,重新申請大塊內存耗時會變長,可能會產阻塞風險。另外,如果操作系統開啟了內存大頁機制(Huge Page,頁面大小2M),那么父進程申請內存時阻塞的概率將會大大提高,所以在Redis機器上需要關閉Huge Page機制。Redis每次fork生成RDB或AOF重寫完成后,都可以在Redis log中看到父進程重新申請了多大的內存空間。

當主線程使用后臺子線程執行了一次 fsync,需要再次把新接收的操作記錄寫回磁盤時,如果主線程發現上一次的 fsync 還沒有執行完,那么它就會阻塞。所以,如果后臺子線程執行的 fsync 頻繁阻塞的話(比如 AOF 重寫占用了大量的磁盤 IO 帶寬),主線程也會阻塞,導致 Redis 性能變慢。


如果業務應用對延遲非常敏感,但同時允許一定量的數據丟失,那么,可以把配置項 no-appendfsync-on-rewrite 設置為 yes。這個配置項設置為 yes 時,表示在 AOF 重寫時,不進行 fsync 操作。也就是說,Redis 實例把寫命令寫到內存后,不調用后臺線程進行 fsync 操作,就可以直接返回了。

落盤時機和重寫機制都是在“記日志”這一過程中發揮作用的。例如,落盤時機的選擇可以避免記日志時阻塞主線程,重寫可以避免日志文件過大。但是,在“用日志”的過程中,也就是使用 AOF 進行故障恢復時,我們仍然需要把所有的操作記錄都運行一遍。再加上 Redis 的單線程設計,這些命令操作只能一條一條按順序執行,這個“重放”的過程就會很慢了。
有沒有既能避免數據丟失,又能更快地恢復的方法呢?當然有,那就是 RDB 快照了。

摘抄:《Redis 核心技術與實戰》-第4節

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

推薦閱讀更多精彩內容