<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          美團(tuán)面試:宕機(jī)了,Redis 如何避免數(shù)據(jù)丟失?

          共 8372字,需瀏覽 17分鐘

           ·

          2023-07-27 08:58

          Redis 持久化機(jī)制屬于后端面試超高頻的面試知識(shí)點(diǎn),老生常談了,需要重點(diǎn)花時(shí)間掌握。即使不是準(zhǔn)備面試,日常開發(fā)也是需要經(jīng)常用到的。

          最近抽空對(duì)之前寫的 Redis 持久化機(jī)制進(jìn)行了大幅完善,圖文并茂,清晰易懂。分享一下,希望對(duì)你有幫助!

          內(nèi)容概覽:

          下面是正文。

          使用緩存的時(shí)候,我們經(jīng)常需要對(duì)內(nèi)存中的數(shù)據(jù)進(jìn)行持久化也就是將內(nèi)存中的數(shù)據(jù)寫入到硬盤中。大部分原因是為了之后重用數(shù)據(jù)(比如重啟機(jī)器、機(jī)器故障之后恢復(fù)數(shù)據(jù)),或者是為了做數(shù)據(jù)同步(比如 Redis 集群的主從節(jié)點(diǎn)通過 RDB 文件同步數(shù)據(jù))。

          Redis 不同于 Memcached 的很重要一點(diǎn)就是,Redis 支持持久化,而且支持 3 種持久化方式:

          • 快照(snapshotting,RDB)
          • 只追加文件(append-only file, AOF)
          • RDB 和 AOF 的混合持久化(Redis 4.0 新增)

          官方文檔地址:https://redis.io/topics/persistence 。

          RDB 持久化

          什么是 RDB 持久化?

          Redis 可以通過創(chuàng)建快照來獲得存儲(chǔ)在內(nèi)存里面的數(shù)據(jù)在 某個(gè)時(shí)間點(diǎn) 上的副本。Redis 創(chuàng)建快照之后,可以對(duì)快照進(jìn)行備份,可以將快照復(fù)制到其他服務(wù)器從而創(chuàng)建具有相同數(shù)據(jù)的服務(wù)器副本(Redis 主從結(jié)構(gòu),主要用來提高 Redis 性能),還可以將快照留在原地以便重啟服務(wù)器的時(shí)候使用。

          快照持久化是 Redis 默認(rèn)采用的持久化方式,在 redis.conf 配置文件中默認(rèn)有此下配置:

          save 900 1           #在900秒(15分鐘)之后,如果至少有1個(gè)key發(fā)生變化,Redis就會(huì)自動(dòng)觸發(fā)bgsave命令創(chuàng)建快照。

          save 300 10          #在300秒(5分鐘)之后,如果至少有10個(gè)key發(fā)生變化,Redis就會(huì)自動(dòng)觸發(fā)bgsave命令創(chuàng)建快照。

          save 60 10000        #在60秒(1分鐘)之后,如果至少有10000個(gè)key發(fā)生變化,Redis就會(huì)自動(dòng)觸發(fā)bgsave命令創(chuàng)建快照。

          RDB 創(chuàng)建快照時(shí)會(huì)阻塞主線程嗎?

          Redis 提供了兩個(gè)命令來生成 RDB 快照文件:

          • save : 同步保存操作,會(huì)阻塞 Redis 主線程;
          • bgsave : fork 出一個(gè)子進(jìn)程,子進(jìn)程執(zhí)行,不會(huì)阻塞 Redis 主線程,默認(rèn)選項(xiàng)。

          這里說 Redis 主線程而不是主進(jìn)程的主要是因?yàn)?Redis 啟動(dòng)之后主要是通過單線程的方式完成主要的工作。如果你想將其描述為 Redis 主進(jìn)程,也沒毛病。

          AOF 持久化

          什么是 AOF 持久化?

          與快照持久化相比,AOF 持久化的實(shí)時(shí)性更好。默認(rèn)情況下 Redis 沒有開啟 AOF(append only file)方式的持久化(Redis 6.0 之后已經(jīng)默認(rèn)是開啟了),可以通過 appendonly 參數(shù)開啟:

          appendonly yes

          開啟 AOF 持久化后每執(zhí)行一條會(huì)更改 Redis 中的數(shù)據(jù)的命令,Redis 就會(huì)將該命令寫入到 AOF 緩沖區(qū) server.aof_buf 中,然后再寫入到 AOF 文件中(此時(shí)還在系統(tǒng)內(nèi)核緩存區(qū)未同步到磁盤),最后再根據(jù)持久化方式( fsync策略)的配置來決定何時(shí)將系統(tǒng)內(nèi)核緩存區(qū)的數(shù)據(jù)同步到硬盤中的。

          只有同步到磁盤中才算持久化保存了,否則依然存在數(shù)據(jù)丟失的風(fēng)險(xiǎn),比如說:系統(tǒng)內(nèi)核緩存區(qū)的數(shù)據(jù)還未同步,磁盤機(jī)器就宕機(jī)了,那這部分?jǐn)?shù)據(jù)就算丟失了。

          AOF 文件的保存位置和 RDB 文件的位置相同,都是通過 dir 參數(shù)設(shè)置的,默認(rèn)的文件名是 appendonly.aof。

          AOF 工作基本流程是怎樣的?

          AOF 持久化功能的實(shí)現(xiàn)可以簡單分為 5 步:

          1. 命令追加(append):所有的寫命令會(huì)追加到 AOF 緩沖區(qū)中。
          2. 文件寫入(write):將 AOF 緩沖區(qū)的數(shù)據(jù)寫入到 AOF 文件中。這一步需要調(diào)用write函數(shù)(系統(tǒng)調(diào)用),write將數(shù)據(jù)寫入到了系統(tǒng)內(nèi)核緩沖區(qū)之后直接返回了(延遲寫)。注意?。?!此時(shí)并沒有同步到磁盤。
          3. 文件同步(fsync):AOF 緩沖區(qū)根據(jù)對(duì)應(yīng)的持久化方式( fsync 策略)向硬盤做同步操作。這一步需要調(diào)用 fsync 函數(shù)(系統(tǒng)調(diào)用), fsync 針對(duì)單個(gè)文件操作,對(duì)其進(jìn)行強(qiáng)制硬盤同步,fsync 將阻塞直到寫入磁盤完成后返回,保證了數(shù)據(jù)持久化。
          4. 文件重寫(rewrite):隨著 AOF 文件越來越大,需要定期對(duì) AOF 文件進(jìn)行重寫,達(dá)到壓縮的目的。
          5. 重啟加載(load):當(dāng) Redis 重啟時(shí),可以加載 AOF 文件進(jìn)行數(shù)據(jù)恢復(fù)。

          Linux 系統(tǒng)直接提供了一些函數(shù)用于對(duì)文件和設(shè)備進(jìn)行訪問和控制,這些函數(shù)被稱為 系統(tǒng)調(diào)用(syscall)。

          這里對(duì)上面提到的一些 Linux 系統(tǒng)調(diào)用再做一遍解釋:

          • write:寫入系統(tǒng)內(nèi)核緩沖區(qū)之后直接返回(僅僅是寫到緩沖區(qū)),不會(huì)立即同步到硬盤。雖然提高了效率,但也帶來了數(shù)據(jù)丟失的風(fēng)險(xiǎn)。同步硬盤操作通常依賴于系統(tǒng)調(diào)度機(jī)制,Linux 內(nèi)核通常為 30s 同步一次,具體值取決于寫出的數(shù)據(jù)量和 I/O 緩沖區(qū)的狀態(tài)。
          • fsyncfsync用于強(qiáng)制刷新系統(tǒng)內(nèi)核緩沖區(qū)(同步到到磁盤),確保寫磁盤操作結(jié)束才會(huì)返回。

          AOF 工作流程圖如下:

          AOF 工作基本流程

          AOF 持久化方式有哪些?

          在 Redis 的配置文件中存在三種不同的 AOF 持久化方式( fsync策略),它們分別是:

          1. appendfsync always:主線程調(diào)用 write 執(zhí)行寫操作后,后臺(tái)線程( aof_fsync 線程)立即會(huì)調(diào)用 fsync 函數(shù)同步 AOF 文件(刷盤),fsync 完成后線程返回,這樣會(huì)嚴(yán)重降低 Redis 的性能(write + fsync)。
          2. appendfsync everysec:主線程調(diào)用 write 執(zhí)行寫操作后立即返回,由后臺(tái)線程( aof_fsync 線程)每秒鐘調(diào)用 fsync 函數(shù)(系統(tǒng)調(diào)用)同步一次 AOF 文件(write+fsync,fsync間隔為 1 秒)
          3. appendfsync no:主線程調(diào)用 write 執(zhí)行寫操作后立即返回,讓操作系統(tǒng)決定何時(shí)進(jìn)行同步,Linux 下一般為 30 秒一次(write但不fsync,fsync 的時(shí)機(jī)由操作系統(tǒng)決定)。

          可以看出:這 3 種持久化方式的主要區(qū)別在于 fsync 同步 AOF 文件的時(shí)機(jī)(刷盤)。

          為了兼顧數(shù)據(jù)和寫入性能,可以考慮 appendfsync everysec 選項(xiàng) ,讓 Redis 每秒同步一次 AOF 文件,Redis 性能受到的影響較小。而且這樣即使出現(xiàn)系統(tǒng)崩潰,用戶最多只會(huì)丟失一秒之內(nèi)產(chǎn)生的數(shù)據(jù)。當(dāng)硬盤忙于執(zhí)行寫入操作的時(shí)候,Redis 還會(huì)優(yōu)雅的放慢自己的速度以便適應(yīng)硬盤的最大寫入速度。

          從 Redis 7.0.0 開始,Redis 使用了 Multi Part AOF 機(jī)制。顧名思義,Multi Part AOF 就是將原來的單個(gè) AOF 文件拆分成多個(gè) AOF 文件。在 Multi Part AOF 中,AOF 文件被分為三種類型,分別為:

          • BASE:表示基礎(chǔ) AOF 文件,它一般由子進(jìn)程通過重寫產(chǎn)生,該文件最多只有一個(gè)。
          • INCR:表示增量 AOF 文件,它一般會(huì)在 AOFRW 開始執(zhí)行時(shí)被創(chuàng)建,該文件可能存在多個(gè)。
          • HISTORY:表示歷史 AOF 文件,它由 BASE 和 INCR AOF 變化而來,每次 AOFRW 成功完成時(shí),本次 AOFRW 之前對(duì)應(yīng)的 BASE 和 INCR AOF 都將變?yōu)?HISTORY,HISTORY 類型的 AOF 會(huì)被 Redis 自動(dòng)刪除。

          Multi Part AOF 不是重點(diǎn),了解即可,詳細(xì)介紹可以看看阿里開發(fā)者的Redis 7.0 Multi Part AOF 的設(shè)計(jì)和實(shí)現(xiàn)[1] 這篇文章。

          相關(guān) issueRedis 的 AOF 方式 #783[2]

          AOF 為什么是在執(zhí)行完命令之后記錄日志?

          關(guān)系型數(shù)據(jù)庫(如 MySQL)通常都是執(zhí)行命令之前記錄日志(方便故障恢復(fù)),而 Redis AOF 持久化機(jī)制是在執(zhí)行完命令之后再記錄日志。

          AOF 記錄日志過程

          為什么是在執(zhí)行完命令之后記錄日志呢?

          • 避免額外的檢查開銷,AOF 記錄日志不會(huì)對(duì)命令進(jìn)行語法檢查;
          • 在命令執(zhí)行完之后再記錄,不會(huì)阻塞當(dāng)前的命令執(zhí)行。

          這樣也帶來了風(fēng)險(xiǎn)(我在前面介紹 AOF 持久化的時(shí)候也提到過):

          • 如果剛執(zhí)行完命令 Redis 就宕機(jī)會(huì)導(dǎo)致對(duì)應(yīng)的修改丟失;
          • 可能會(huì)阻塞后續(xù)其他命令的執(zhí)行(AOF 記錄日志是在 Redis 主線程中進(jìn)行的)。

          AOF 重寫了解嗎?

          當(dāng) AOF 變得太大時(shí),Redis 能夠在后臺(tái)自動(dòng)重寫 AOF 產(chǎn)生一個(gè)新的 AOF 文件,這個(gè)新的 AOF 文件和原有的 AOF 文件所保存的數(shù)據(jù)庫狀態(tài)一樣,但體積更小。

          AOF 重寫

          AOF 重寫(rewrite) 是一個(gè)有歧義的名字,該功能是通過讀取數(shù)據(jù)庫中的鍵值對(duì)來實(shí)現(xiàn)的,程序無須對(duì)現(xiàn)有 AOF 文件進(jìn)行任何讀入、分析或者寫入操作。

          由于 AOF 重寫會(huì)進(jìn)行大量的寫入操作,為了避免對(duì) Redis 正常處理命令請(qǐng)求造成影響,Redis 將 AOF 重寫程序放到子進(jìn)程里執(zhí)行。

          AOF 文件重寫期間,Redis 還會(huì)維護(hù)一個(gè) AOF 重寫緩沖區(qū),該緩沖區(qū)會(huì)在子進(jìn)程創(chuàng)建新 AOF 文件期間,記錄服務(wù)器執(zhí)行的所有寫命令。當(dāng)子進(jìn)程完成創(chuàng)建新 AOF 文件的工作之后,服務(wù)器會(huì)將重寫緩沖區(qū)中的所有內(nèi)容追加到新 AOF 文件的末尾,使得新的 AOF 文件保存的數(shù)據(jù)庫狀態(tài)與現(xiàn)有的數(shù)據(jù)庫狀態(tài)一致。最后,服務(wù)器用新的 AOF 文件替換舊的 AOF 文件,以此來完成 AOF 文件重寫操作。

          開啟 AOF 重寫功能,可以調(diào)用 BGREWRITEAOF 命令手動(dòng)執(zhí)行,也可以設(shè)置下面兩個(gè)配置項(xiàng),讓程序自動(dòng)決定觸發(fā)時(shí)機(jī):

          • auto-aof-rewrite-min-size:如果 AOF 文件大小小于該值,則不會(huì)觸發(fā) AOF 重寫。默認(rèn)值為 64 MB;
          • auto-aof-rewrite-percentage:執(zhí)行 AOF 重寫時(shí),當(dāng)前 AOF 大?。╝of_current_size)和上一次重寫時(shí) AOF 大?。╝of_base_size)的比值。如果當(dāng)前 AOF 文件大小增加了這個(gè)百分比值,將觸發(fā) AOF 重寫。將此值設(shè)置為 0 將禁用自動(dòng) AOF 重寫。默認(rèn)值為 100。

          Redis 7.0 版本之前,如果在重寫期間有寫入命令,AOF 可能會(huì)使用大量內(nèi)存,重寫期間到達(dá)的所有寫入命令都會(huì)寫入磁盤兩次。

          Redis 7.0 版本之后,AOF 重寫機(jī)制得到了優(yōu)化改進(jìn)。下面這段內(nèi)容摘自阿里開發(fā)者的從 Redis7.0 發(fā)布看 Redis 的過去與未來 這篇文章。

          AOF 重寫期間的增量數(shù)據(jù)如何處理一直是個(gè)問題,在過去寫期間的增量數(shù)據(jù)需要在內(nèi)存中保留,寫結(jié)束后再把這部分增量數(shù)據(jù)寫入新的 AOF 文件中以保證數(shù)據(jù)完整性。可以看出來 AOF 寫會(huì)額外消耗內(nèi)存和磁盤 IO,這也是 Redis AOF 寫的痛點(diǎn),雖然之前也進(jìn)行過多次改進(jìn)但是資源消耗的本質(zhì)問題一直沒有解決。

          阿里云的 Redis 企業(yè)版在最初也遇到了這個(gè)問題,在內(nèi)部經(jīng)過多次迭代開發(fā),實(shí)現(xiàn)了 Multi-part AOF 機(jī)制來解決,同時(shí)也貢獻(xiàn)給了社區(qū)并隨此次 7.0 發(fā)布。具體方法是采用 base(全量數(shù)據(jù))+inc(增量數(shù)據(jù))獨(dú)立文件存儲(chǔ)的方式,徹底解決內(nèi)存和 IO 資源的浪費(fèi),同時(shí)也支持對(duì)歷史 AOF 文件的保存管理,結(jié)合 AOF 文件中的時(shí)間信息還可以實(shí)現(xiàn) PITR 按時(shí)間點(diǎn)恢復(fù)(阿里云企業(yè)版 Tair 已支持),這進(jìn)一步增強(qiáng)了 Redis 的數(shù)據(jù)可靠性,滿足用戶數(shù)據(jù)回檔等需求。

          相關(guān) issueRedis AOF 重寫描述不準(zhǔn)確 #1439[3]。

          AOF 校驗(yàn)機(jī)制了解嗎?

          AOF 校驗(yàn)機(jī)制是 Redis 在啟動(dòng)時(shí)對(duì) AOF 文件進(jìn)行檢查,以判斷文件是否完整,是否有損壞或者丟失的數(shù)據(jù)。這個(gè)機(jī)制的原理其實(shí)非常簡單,就是通過使用一種叫做 校驗(yàn)和(checksum) 的數(shù)字來驗(yàn)證 AOF 文件。這個(gè)校驗(yàn)和是通過對(duì)整個(gè) AOF 文件內(nèi)容進(jìn)行 CRC64 算法計(jì)算得出的數(shù)字。如果文件內(nèi)容發(fā)生了變化,那么校驗(yàn)和也會(huì)隨之改變。因此,Redis 在啟動(dòng)時(shí)會(huì)比較計(jì)算出的校驗(yàn)和與文件末尾保存的校驗(yàn)和(計(jì)算的時(shí)候會(huì)把最后一行保存校驗(yàn)和的內(nèi)容給忽略點(diǎn)),從而判斷 AOF 文件是否完整。如果發(fā)現(xiàn)文件有問題,Redis 就會(huì)拒絕啟動(dòng)并提供相應(yīng)的錯(cuò)誤信息。AOF 校驗(yàn)機(jī)制十分簡單有效,可以提高 Redis 數(shù)據(jù)的可靠性。

          類似地,RDB 文件也有類似的校驗(yàn)機(jī)制來保證 RDB 文件的正確性,這里就不重復(fù)進(jìn)行介紹了。

          Redis 4.0 對(duì)于持久化機(jī)制做了什么優(yōu)化?

          由于 RDB 和 AOF 各有優(yōu)勢,于是,Redis 4.0 開始支持 RDB 和 AOF 的混合持久化(默認(rèn)關(guān)閉,可以通過配置項(xiàng) aof-use-rdb-preamble 開啟)。

          如果把混合持久化打開,AOF 重寫的時(shí)候就直接把 RDB 的內(nèi)容寫到 AOF 文件開頭。這樣做的好處是可以結(jié)合 RDB 和 AOF 的優(yōu)點(diǎn), 快速加載同時(shí)避免丟失過多的數(shù)據(jù)。當(dāng)然缺點(diǎn)也是有的, AOF 里面的 RDB 部分是壓縮格式不再是 AOF 格式,可讀性較差。

          官方文檔地址:https://redis.io/topics/persistence

          如何選擇 RDB 和 AOF?

          關(guān)于 RDB 和 AOF 的優(yōu)缺點(diǎn),官網(wǎng)上面也給了比較詳細(xì)的說明Redis persistence[4],這里結(jié)合自己的理解簡單總結(jié)一下。

          RDB 比 AOF 優(yōu)秀的地方

          • RDB 文件存儲(chǔ)的內(nèi)容是經(jīng)過壓縮的二進(jìn)制數(shù)據(jù), 保存著某個(gè)時(shí)間點(diǎn)的數(shù)據(jù)集,文件很小,適合做數(shù)據(jù)的備份,災(zāi)難恢復(fù)。AOF 文件存儲(chǔ)的是每一次寫命令,類似于 MySQL 的 binlog 日志,通常會(huì)比 RDB 文件大很多。當(dāng) AOF 變得太大時(shí),Redis 能夠在后臺(tái)自動(dòng)重寫 AOF。新的 AOF 文件和原有的 AOF 文件所保存的數(shù)據(jù)庫狀態(tài)一樣,但體積更小。不過, Redis 7.0 版本之前,如果在重寫期間有寫入命令,AOF 可能會(huì)使用大量內(nèi)存,重寫期間到達(dá)的所有寫入命令都會(huì)寫入磁盤兩次。
          • 使用 RDB 文件恢復(fù)數(shù)據(jù),直接解析還原數(shù)據(jù)即可,不需要一條一條地執(zhí)行命令,速度非??臁6?AOF 則需要依次執(zhí)行每個(gè)寫命令,速度非常慢。也就是說,與 AOF 相比,恢復(fù)大數(shù)據(jù)集的時(shí)候,RDB 速度更快。

          AOF 比 RDB 優(yōu)秀的地方

          • RDB 的數(shù)據(jù)安全性不如 AOF,沒有辦法實(shí)時(shí)或者秒級(jí)持久化數(shù)據(jù)。生成 RDB 文件的過程是比較繁重的, 雖然 BGSAVE 子進(jìn)程寫入 RDB 文件的工作不會(huì)阻塞主線程,但會(huì)對(duì)機(jī)器的 CPU 資源和內(nèi)存資源產(chǎn)生影響,嚴(yán)重的情況下甚至?xí)苯影?Redis 服務(wù)干宕機(jī)。AOF 支持秒級(jí)數(shù)據(jù)丟失(取決 fsync 策略,如果是 everysec,最多丟失 1 秒的數(shù)據(jù)),僅僅是追加命令到 AOF 文件,操作輕量。
          • RDB 文件是以特定的二進(jìn)制格式保存的,并且在 Redis 版本演進(jìn)中有多個(gè)版本的 RDB,所以存在老版本的 Redis 服務(wù)不兼容新版本的 RDB 格式的問題。
          • AOF 以一種易于理解和解析的格式包含所有操作的日志。你可以輕松地導(dǎo)出 AOF 文件進(jìn)行分析,你也可以直接操作 AOF 文件來解決一些問題。比如,如果執(zhí)行FLUSHALL命令意外地刷新了所有內(nèi)容后,只要 AOF 文件沒有被重寫,刪除最新命令并重啟即可恢復(fù)之前的狀態(tài)。

          綜上

          • Redis 保存的數(shù)據(jù)丟失一些也沒什么影響的話,可以選擇使用 RDB。
          • 不建議單獨(dú)使用 AOF,因?yàn)闀r(shí)不時(shí)地創(chuàng)建一個(gè) RDB 快照可以進(jìn)行數(shù)據(jù)庫備份、更快的重啟以及解決 AOF 引擎錯(cuò)誤。
          • 如果保存的數(shù)據(jù)要求安全性比較高的話,建議同時(shí)開啟 RDB 和 AOF 持久化或者開啟 RDB 和 AOF 混合持久化。

          參考資料

          [1]

          Redis 7.0 Multi Part AOF 的設(shè)計(jì)和實(shí)現(xiàn): https://zhuanlan.zhihu.com/p/467217082

          [2]

          Redis 的 AOF 方式 #783: https://github.com/Snailclimb/JavaGuide/issues/783

          [3]

          Redis AOF 重寫描述不準(zhǔn)確 #1439: https://github.com/Snailclimb/JavaGuide/issues/1439

          [4]

          Redis persistence: https://redis.io/docs/manual/persistence/

          ··············  END  ··············

          瀏覽 34
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日逼国产 | 黄色网页大全 | 日本国产在线 | 国产色欲一区二区精品 | 日本一区二区在线视频 |