深入淺出AOF功能和AOF重寫兩個知識點

本文默認你是使用過redis并了解redis的基礎(chǔ)概念,學(xué)習(xí)redis入門并不難,給你介紹各種API使用也沒啥意思。在這里 不會給你堆各種專業(yè)詞匯,只有我個人理解的大白話

這里沒別的 先來張總原理圖,花10秒時間記一下,跟游戲打副本一樣,先看下地圖 做到腦子里有個大概的脈絡(luò),這里也是平時工作中比較常用的一種工作思考方式,遇到難的問題如果沒思路就先忙別的,我們的大腦會在后臺保持這個問題并且會時不時的激活一下,同樣的時間做了多件事。高考時老師說拿到試卷先看一遍最后的作文題目在開始答題,一樣的思想

AOF功能工作原理和AOF重寫原理總圖:


QQ截圖20200514104327.png

10秒已過 ,開始正文
一。AOF
1.說白了就是個日志記錄文件。redis會把所有寫命令記錄到一個設(shè)定好的日志文件中,做開發(fā)的都知道 存儲日志 是需要有 特定的格式的,這樣后面你才能快速的定位檢索,AOF里存儲的是Redis自己定義的RESP協(xié)議格式的字符串,對,你沒 看錯就是字符串,(resp協(xié)議的https://blog.csdn.net/wenmeishuai/article/details/106101762
2.AOF里記錄的是每一次寫命令,比如說list數(shù)據(jù)類型,其中有100條數(shù)據(jù),在AOF中可能就有100條RESP協(xié)議的記錄, 參考RESP的文章就知道,這其中有大量的重復(fù)命令

3.開啟AOF持久化功能只需改兩個配置參數(shù)
    appendonly yes 開啟aof
    appendfilename mads.aof 日志文件名字,隨便起
4.人是個喜歡刨根問底的生物,用是會用了。它是怎么實現(xiàn)的?
    1)上面圖中 紅色框內(nèi)是AOF的流程。 主進程接收客戶端請求寫命令,寫入到aof_buf(aof緩沖區(qū))然后主進程就返回了                    (redis的優(yōu)化點)
    2)有專門的子進程去調(diào)用fsync()函數(shù)把數(shù)據(jù)從aof_buf寫入到aof文件(誰在說redis是單線程就對他說:哼 初級程序員)
    3)結(jié)合上面2)子進程該什么時候去觸發(fā)調(diào)用fsync()這個同步動作呢,redis已經(jīng)幫我們提前設(shè)置了三種策略:
        ps:fsync()函數(shù)要求操作系統(tǒng)馬上把aof_buf數(shù)據(jù)寫到磁盤上.
        配置文件中
            appendfsync always 
            appendfsync everysec 
            appendfsync no  
            # no:不要立刻刷,只有在操作系統(tǒng)需要刷的時候再刷 ,比較快。如果redis重啟了拿到的數(shù)據(jù)將不是很新的數(shù)據(jù) 
            # always:每次寫操作都立刻寫入到aof文件。慢,但是最安全。 
            # everysec:每秒寫一次。折衷方案。 (默認,如果拿不準就用 "everysec"試水 )
     4)AOF追加阻塞
        結(jié)合上面繼續(xù)深入,如果上面我設(shè)置的是每1秒同步一次數(shù)據(jù),在線上大批量寫請求下aof_buf有大量數(shù)據(jù)需要同步,此時就會對磁盤進行高頻寫入,磁盤IO就變 
       成了瓶頸,就會出現(xiàn)上次的同步動作還沒完成
        主進程又接收到大批寫命令寫到了緩沖區(qū),此時redis為了保證aof文件安全性,會阻塞主線程,直到上次fsync同步完成。
        主進程吧數(shù)據(jù)寫入到aof_buf后會對比上次fsync操作時間,此時有兩種情況:
            1.如果距離上次fsync操作時間大于2S則阻塞主進程直到fsync結(jié)束
            2.如果距上次操作時間小于2S則主進程直接返回
        這里雖然我們配置的是每秒同步一次,但是實際上不是丟失1S的數(shù)據(jù),實際上可能丟失2S數(shù)據(jù),這里請細品
     5)aof_buf同步到文件的流程  執(zhí)行之前,看總量,依次執(zhí)行,執(zhí)行完成了再回收(清空)緩沖區(qū)
              ![20200513134047790.png](https://upload-images.jianshu.io/upload_images/20421881-260636d267ea1789.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
     6)aof_buf里存儲的格式是  RESP協(xié)議格式,(還沒有看源碼確認)

二。AOF重寫
1.通過上面已經(jīng)知道了AOF記錄的是字符串。真正線上環(huán)境寫操作是很多的,AOF的文件大小增加也是很快的,如果一個 AOF文件有10G了再去追加的時候那是非
常慢的了。
2. redis就提供了一種壓縮的方式,比如還是上面list數(shù)據(jù)類型100條數(shù)據(jù),經(jīng)過壓縮以后就變成了一條,這就是AOF重寫。
3.AOF重寫會觸發(fā)Redis的緩存淘汰策略,可以參考aof_rewrite函數(shù)源碼,參考這篇文章
https://blog.csdn.net/qq_41453285/article/details/103301715

具體流程:
1.Redis開啟了持久化功能,并且達到了重寫的條件。三個參數(shù),
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 自動重寫AOF文件
# 如果AOF日志文件大到指定百分比,Redis能夠通過 BGREWRITEAOF 自動重寫AOF日志文件。
# 工作原理:Redis記住上次重寫時AOF日志的大小(或者重啟后沒有寫操作的話,那就直接用此時的AOF文件),
# 基準尺寸和當(dāng)前尺寸做比較。如果當(dāng)前尺寸超過指定比例,就會觸發(fā)重寫操作。
# 你還需要指定被重寫日志的最小尺寸,這樣避免了達到約定百分比但尺寸仍然很小的情況還要重寫。
# 指定百分比為0會禁用AOF自動重寫特性。

2.調(diào)用fork系統(tǒng)級別函數(shù),復(fù)制出完全一致的一個子進程,和主進程共用同一塊內(nèi)存空間(類似淺復(fù)制,redis為了節(jié)省內(nèi)存開銷的優(yōu)化點)
3.子進程調(diào)用aof_rewrite函數(shù)(redis客戶端執(zhí)行bgrewriteaof命令最終也是調(diào)用此函數(shù))可以創(chuàng)建新的AOF文件去執(zhí)行重寫操作根據(jù)已有數(shù)據(jù)進行命令的壓縮和過期時間的檢測并 
   將壓縮后的命令寫入到新的AOF文件,直到寫完
4.在AOF重寫過程中,主進程是可以繼續(xù)對外服務(wù)的,當(dāng)接收到寫命令,寫入到aof_buf后,然后判斷此時是否正在執(zhí)行重寫 操作,如果是再將寫命令寫入 到AOF重寫緩沖 
  區(qū),主進程返回
5.當(dāng)子進程完成對AOF文件重寫之后,它會向父進程發(fā)送一個完成信號,父進程接到該完成信號之后,會調(diào)用一個信號處理函數(shù),該函數(shù)完成以下工作:
     1).將AOF重寫緩存中的內(nèi)容全部寫入到新的AOF文件中;這個時候新的AOF文件所保存的數(shù)據(jù)庫狀態(tài)和服務(wù)器當(dāng)前的數(shù) 據(jù)庫狀態(tài)一致;
     2).對新的AOF文件進行改名,原子的覆蓋原有的AOF文件;完成新舊兩個AOF文件的替換。到這里才是一次完整的AOF重寫流程
     3).當(dāng)這個信號處理函數(shù)執(zhí)行完畢之后,主進程就可以繼續(xù)像往常一樣接收命令請求了。在整個AOF后臺重寫過程中,只有最后的“主進程寫入命令到AOF緩存”和“對新的 
        AOF文件進行改名,覆蓋原有的 AOF文件?!边@兩個步驟(信號處理函數(shù)執(zhí)行期間)會造成主進程阻塞,在其他時候,AOF后臺重寫都不會對主進程造成阻塞,這將AOF 
        重寫對性能造成的影響降到最低。
  6.如果AOF重寫失敗 redis會刪除中間臨時產(chǎn)物,保證流程的健壯性

AOF后臺重寫為什么這么干
1.aof_rewrite這個函數(shù)會進行大量的寫入 操作,所以調(diào)用這個函數(shù)的線程將被長時間的阻塞,因為Redis服務(wù)器使用單線程來處理命令請求;所以如果直接是服務(wù)器進 程調(diào)用
AOF_REWRITE函數(shù)的話,那么重寫AOF期間,服務(wù)器將無法處理客戶端發(fā)送來的命令請求;
2.Redis不希望AOF重寫會造成服務(wù)器無法處理請求,所以Redis決定將AOF重寫程序放到子進程(后臺)里執(zhí)行。這樣處理的最 大好處是:
1)子進程進行AOF重寫期間,主進程可以繼續(xù)處理命令請求;
2)子進程帶有主進程的數(shù)據(jù)副本,使用子進程而不是線程,可以避免在鎖的情況下,保證數(shù)據(jù)的安全性。
使用子進程進行AOF重寫的問題
3.子進程在進行AOF重寫期間,服務(wù)器進程還要繼續(xù)處理命令請求,而新的命令可能對現(xiàn)有的數(shù)據(jù)進行修改,這會讓當(dāng)前數(shù)據(jù)庫的數(shù)據(jù)和重寫后的AOF文件中的數(shù)據(jù)不一致。
如何修正
1.為了解決這種數(shù)據(jù)不一致的問題,Redis增加了一個AOF重寫緩存,這個緩存在fork出子進程之后開始啟用,Redis服務(wù)器主進程在執(zhí)行完寫命令之后,會同時將這個寫命令
追加到AOF緩沖區(qū)和AOF重寫緩沖區(qū)
2.即子進程在執(zhí)行AOF重寫時,主進程需要執(zhí)行以下三個工作:
執(zhí)行client發(fā)來的命令請求;
將寫命令追加到現(xiàn)有的AOF文件中;
將寫命令追加到AOF重寫緩存中。

最后記錄下我的思考
1.寫文件是很耗時的,數(shù)據(jù)多時還會出現(xiàn)阻塞。熟悉操作系統(tǒng)的同學(xué)會有個印象,數(shù)據(jù)從緩沖區(qū)到磁盤的過程也要經(jīng)過內(nèi) 核態(tài)到用戶態(tài)的轉(zhuǎn)換出現(xiàn)上下文切
換,這個過程會很長,(Netty零拷貝可以看看)
2.Redis設(shè)計初衷就是快,10W+的QPS 也就是1ms就要處理100個命令,阻塞對于redis無疑是不可接受的,如果你是 redis作者會怎么解決呢,很簡單的能想到就是同步
轉(zhuǎn)異步嘛,讓會產(chǎn)生阻塞或者耗時的操作由子線程去干就好了,主進程就專注處理客戶端請求就好了(重點記憶,主進程只跟客戶端打交道,其他全交給子進程),專
業(yè)的人干專業(yè) 的事,人類發(fā)展史中 分工產(chǎn)生效能 也是這個思想。
3.追加阻塞這里,為什么不采用隊列或多線程的方式來讓主進程迅速返回?考慮是 增加額外的處理肯定要增加技術(shù)復(fù)雜 度和資源的消耗,。。。
4.其實最后發(fā)現(xiàn)并沒有什么高大上的解決方案對不對?;A(chǔ)真的很重要呀。并且優(yōu)化是從各個細節(jié)來的,也是個持續(xù)的過程,成熟的架構(gòu)都是慢慢演進的,

最后拋幾個問題看看掌握沒有

1.redis是單線程么?
2.為什么要子進程呢?因為萬一重寫失敗。不會造成redis的死掉
3.為什么使用子進程而不是線程?可以避免在鎖的情況下,保證數(shù)據(jù)的安全性。
4.為什么主子公用內(nèi)存空間? 省空間,redis是內(nèi)存數(shù)據(jù)庫,留著內(nèi)存干正事不香么

redis配置參數(shù)很全的一篇
https://www.cnblogs.com/xuliangxing/p/7151685.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容