1. Redis 的持久化剖析,通俗易懂

        共 5904字,需瀏覽 12分鐘

         ·

        2021-08-17 03:02

        關注我們,設為星標,每天7:30不見不散,架構路上與您共享 

        回復"架構師"獲取資源


        Redis 是一個非關系型的內(nèi)存數(shù)據(jù)庫,使用內(nèi)存存儲數(shù)據(jù)是它能夠進行快速存取數(shù)據(jù)的原因之一。

        在實際應用中,常有人提倡把 Redis 只作為一種能夠提高用戶體驗的組件來使用, 也就是說即使 Redis 服務掛掉之后也要保證系統(tǒng)正常使用。不過,在很多系統(tǒng)中還是希望既能發(fā)揮 Redis 基于內(nèi)存快速存取的特性,又希望機器斷電或 Redis服務停止后數(shù)據(jù)不丟失。所以,才引出了 Redis 的持久化功能。

        在許多技術文章中,提到 Redis 的持久化時往往都會直接拋出兩個名詞 RDB 和 AOF。然后接下來就是分別介紹這兩個名詞。當然,如果要談 Redis 的持久化肯定避免不了講 RDB 和 AOF,但這是介紹持久化最恰當?shù)姆绞絾??這樣的文章是不是顯得有些生硬呢?所以,在嘗試弄明白一個事物的原理時一定要從頭到尾的思考它存在的意義?為了解決什么問題?采用了什么方式?達到了什么目的?自己有沒有其它的方案?這樣從問題的源頭切入,才能對這個事物理解的更加深刻,從而能夠更好的幫助自己進行舉一反三。而不是人云亦云,對于一些知識僅僅是背誦下來,這種死記硬背下來的知識在腦海里的保質(zhì)期也是短的可憐。

        在前面,我們已經(jīng)提到為什么需要引入持久化?簡單的來說持久化就是把內(nèi)存中的數(shù)據(jù)存儲到外存上,這樣服務停止后,當再啟動的時候就可以把外存的數(shù)據(jù)讀取到內(nèi)存中從而達到了不丟失數(shù)據(jù)的目的。

        1、RDB

        如果讓你設計一個持久化的方案,你會怎么做呢?(假裝絞盡腦汁… …)首先,我們可以使用一種簡單的策略,將 Redis 中所有的數(shù)據(jù)按照一定格式全部寫到磁盤上,即創(chuàng)建數(shù)據(jù)的快照文件。然后,你為了盡量保證不丟數(shù)據(jù)需要考慮使用實時寫還是定時寫,又或者用其它策略。其實,現(xiàn)在的你已經(jīng)在嘗試著去實現(xiàn) RDB (Redis Database)持久化的機制了。所以,你看它其實并不難。萬丈高樓從地起,先從一個簡單的 idea 開始,逐漸去完善它,豐富它的過程便是解決問題的過程。例如用這種思路去學習計算機網(wǎng)絡也是同樣適用的,你可以給自己出一個問題“如何讓兩臺電腦進行通信?”,自己想辦法解決這個問題的過程肯定會比在計算機網(wǎng)絡課堂上收獲的知識更多,也更牢固。

        盡管不需要我們寫代碼來實現(xiàn) RDB 持久化,但是并不妨礙我們來思考一下假如讓我們來實現(xiàn)的話大概會遇到哪些問題?例如:什么時候生成數(shù)據(jù)快照?文件數(shù)據(jù)格式的定義?如果在主進程中進行持久化,阻塞客戶端的請求后會不會有影響?接下來,我們就看一下 RDB 是如何做的吧。

        1.1基本命令

        在 Redis 中,提供了兩個 RDB 持久化的命令: SAVE 和 BGSAVE 。執(zhí)行 SAVE 時,Redis 服務會停止處理任何客戶端的命令請求;執(zhí)行 BGSAVE 時,Redis 服務則會創(chuàng)建一個子進程,由子進程來負責數(shù)據(jù)的持久化,而此時 Redis 服務就可以正常處理客戶端的請求。

        BGSAVE 解決了我們對于持久化時是否會影響 Redis 服務處理客戶端的請求的擔心。

        1.2自動間隔性保存

        自動間隔性保存,則解決了“什么時候生成數(shù)據(jù)快照?”的問題。在 Redis 的配置文件中我們可以寫入以下配置:

        save 600 1
        save 300 10
        save 60 100
        save 30 1000

        上面的配置表示,如果在 600 秒內(nèi)對數(shù)據(jù)庫進行了 1 次修改,就執(zhí)行執(zhí)行一次 BGSAVE 命令;如果在 300 秒內(nèi)對數(shù)據(jù)庫進行了 10 次修改,就執(zhí)行一次 BGSAVE 命令;以此類推。你可以根據(jù)你的業(yè)務場景,配置 save 的參數(shù),也不僅僅局限于 4 條配置。

        實現(xiàn)原理

        在 Redis 啟動時,會把上述配置存儲到 Redis 服務器的狀態(tài)中,具體的結構體則是 redisServer,存儲 save 參數(shù)的結構體為 saveparam。

         1 // Redis 服務器狀態(tài)信息結構體
         2 struct redisServer {
         3     // ... ...
         4  
         5     // 記錄多個 save 配置參數(shù)
         6     struct saveparam *saveparams;
         7     // 修改次數(shù)計數(shù)器
         8     long long dirty;
         9     // 上次執(zhí)行保存的時間
        10     time_t lastsave;
        11  
        12     // ... ...
        13 }
        14 // Save 參數(shù)結構體 saveparam
        15 struct saveparam {
        16     // 秒數(shù)
        17     time_t seconds;
        18     // 修改數(shù)
        19     int changes;
        20 }

        看到上面 redisServer 結構體的屬性信息,你心里應該有答案了吧?dirty 表示的是自從上次執(zhí)行 SAVE 或者 BGSAVE 命令完成之后對數(shù)據(jù)庫進行修改的次數(shù);lastsave 表示的是上次成功執(zhí)行SAVE 或者 BGSAVE 命令的時間。這個時候,如果再有個機制能夠定時檢查是否有滿足條件的配置參數(shù)就可以了。

        Redis 提供了一個周期性操作函數(shù) serverCron,每 100 ms 會執(zhí)行一次。它其中的一項工作就是來檢查是否有符合條件的 save 參數(shù),如果存在符合條件的參數(shù)則執(zhí)行 BGSAVE 命令,執(zhí)行完畢之后將 dirty 和 lastsave 的值重置。相信只要有基礎的編程知識,根據(jù)這些變量就能實現(xiàn)這個檢查的過程吧。

        1.3文件結構


        在上圖中,大寫字母的單詞表示的常量,小寫字母單詞則是變量和數(shù)據(jù)。RDB 文件開頭的“REDIS”是我們習慣稱為的魔數(shù),類似于 class 文件的 COFFEE,用來識別文件類型;緊接著長度為四個字節(jié)的 db_version 記錄的是 RDB 文件的版本號;database 表示的是所存儲的數(shù)據(jù);EOF 則表明數(shù)據(jù)內(nèi)容結束了;check_sum 的值是整個文件的校驗和,用來檢查文件是否損壞。

        2、AOF

        其實持久化數(shù)據(jù)除了 RDB 這種方式,肯定會有同學能想到另一種方式,就是把服務端執(zhí)行的所有客戶端請求增加、修改和刪除等會改變數(shù)據(jù)的命令全都存儲起來。通過存儲這些命令數(shù)據(jù),在遇到機器宕機和服務進程異常中斷的情況下重啟服務時只要執(zhí)行一遍這些持久化的命令即可恢復之前的數(shù)據(jù)了。(也是一個相當好的辦法呀!)

        原理就是如此,那么問題來了,假如同樣讓你來實現(xiàn)這個過程,你會考慮到哪些問題呢?

        一是性能問題,執(zhí)行完命令之后是否直接將此命令持久化到磁盤上還是由操作系統(tǒng)控制文件同步?在這個問題上如何做取舍?二是文件大小問題,隨著 Redis 服務運行越來越久,數(shù)據(jù)文件勢必會越來越大?應該使用什么辦法解決?… …

        我們來看下 Redis 的 AOF 的過程吧!

        2.1持久化過程

        首先,通過在配置文件中增加一行配置 appendonly yes 來開啟 AOF 持久化。

        像 RDB 機制所依賴 redisServer 結構體中的 saveparams、dirty、lastsave 參數(shù)一樣,AOF 的實現(xiàn)依賴 redisServer 結構體中的 aof_buf 參數(shù)。

        1 struct redisServer{
        2     // ... ...
        3  
        4     // AOF 緩沖區(qū)
        5     sds aof_buf;
        6  
        7     // ... ...
        8 }

        aof_buf 參數(shù)用來以協(xié)議格式緩存會對數(shù)據(jù)進行變更的命令。

        在 Redis 服務器執(zhí)行完命令,并將命令以協(xié)議的格式追加到 aof_buf 緩沖區(qū)之后,在當前這個事件循環(huán)結束之前,Redis 還會調(diào)用一個函數(shù) flushAppendOnlyFile,這個函數(shù)會根據(jù)配置文件中 appendfsync 的值來決定接下來的持久化行為。appendfsync 有三個可選值,分別是 always、everysec、no。

        • always: 將 aof_buf 緩沖區(qū)中的內(nèi)容寫入并同步到 AOF 文件。(性能最低,安全最高)

        • everysec: 將 aof_buf 緩沖區(qū)中的內(nèi)容寫入到 AOF 文件,如果上次同步 AOF 文件的時間距離現(xiàn)在超過一秒鐘,那么再次對 AOF 文件同步,并且這個同步是由一個線程專門負責的。(同時兼顧性能與安全,推薦)

        • no: 將 aof_buf 緩沖區(qū)中的內(nèi)容寫入到 AOF 文件,但并不負責對 AOF 文件的同步,把同步的控制權交由操作系統(tǒng)控制。(性能最高,安全最低)

        以上就是 AOF 持久化的基本過程。

        2.2數(shù)據(jù)載入

        由于命令數(shù)據(jù)是以協(xié)議格式存儲至文件中的,所以在啟動 Redis 服務時檢測到 AOF 文件的存在后會啟動載入程序。(如果 RDB 和 AOF 持久化的文件同時存在則會優(yōu)先載入 AOF 文件數(shù)據(jù))

        啟動載入程序后,其載入過程如下圖所示:

        2.3AOF 重寫

        在前面,我們提到 AOF 的這種機制會造成 AOF 數(shù)據(jù)文件越來越大,并且可能會存在許多無意義的命令。例如,先執(zhí)行了一個命令 set chang xuan ,隨后又執(zhí)行了命令 del chang 。其實這兩條語句都會被持久化到 AOF 文件中,但實際上除了能證明曾經(jīng)執(zhí)行過這兩條命令之外對于我們要持久化數(shù)據(jù)的目的而言并沒有什么作用。

        對此,Redis 提供了 AOF 重寫的機制。

        Redis 的 AOF 重寫其實是根據(jù)當前存儲的數(shù)據(jù),生成命令的過程。并且會采用一些策略盡量減小 AOF 文件的大小,例如對于 List 中的數(shù)據(jù)會盡量使用較少的命令操作較多的數(shù)據(jù)。當然,如果在當前進程中進行重寫處理并且數(shù)據(jù)量特別大的情況下肯定會阻塞客戶端的請求,所以和 RDB 一樣,Redis 提供了 AOF 后臺重寫的機制。

        后臺重寫(BGREWRITEAOF)

        AOF 通過 fork 子進程的方式進行后臺重寫有兩個優(yōu)點:

        1. 重寫期間服務器進程可以繼續(xù)處理請求。

        2. 子進程帶有服務器進程的數(shù)據(jù)副本,能充分利用操作系統(tǒng)提供的寫時復制機制從而提升效率,還可以在避免使用鎖的情況下保證數(shù)據(jù)的安全性。

        天下沒有免費的午餐,這種方式還帶來一個問題。就是在使用子進程重寫期間,如果父進程還在處理著客戶端請求,如何保證重寫后 AOF 文件數(shù)據(jù)的一致性呢?

        對于這個問題,Redis 設置了一個 AOF 重寫緩沖區(qū)。在子進程被創(chuàng)建后,Redis 服務器就會啟用這個重寫緩沖區(qū)。在將命令以協(xié)議格式追加到 AOF 緩沖區(qū)之后,同時也會追加到 AOF 重寫緩沖區(qū)。

        當子進程完成重寫工作后會向父進程發(fā)送一個信號。父進程接收到信后之后會進行調(diào)用相關函數(shù),進行以下工作:

        1. 將 AOF 重寫緩沖區(qū)中的內(nèi)容寫入到新的 AOF 文件中。

        2. 對新的 AOF 文件進行改名,原子地覆蓋現(xiàn)有的 AOF 文件,完成新舊文件的替換。

        這時,就完成了一次 AOF 后臺重寫。

        3、總結

        通過前文內(nèi)容,我們可以大致清楚 Redis 所提供的 RDB 和 AOF 兩種持久化機制的過程以及基本原理。它們各有特點,也各有適合使用的場景所以并不能說誰一定比誰好。通過搭配使用,能夠確保線上環(huán)境數(shù)據(jù)的安全性就是最好的。


        文章來源:https://urlify.cn/faauM3



        到此文章就結束了。如果今天的文章對你在進階架構師的路上有新的啟發(fā)和進步,歡迎轉(zhuǎn)發(fā)給更多人。歡迎加入架構師社區(qū)技術交流群,眾多大咖帶你進階架構師,在后臺回復“加群”即可入群。







        這些年小編給你分享過的干貨

        1.SpringBoot物流管理項目,拿去學習吧(附源碼)

        2.ERP系統(tǒng),自帶進銷存+財務+生產(chǎn)功能,拿來即用(附源碼)

        3.帶工作流的SpringBoot后臺管理項目快速開發(fā)(附源碼)
        4.最好的OA系統(tǒng),拿來即用,非常方便(附源碼)

        5.SpringBoot+Vue完整的外賣系統(tǒng),手機端和后臺管理,附源碼!

        6.SpringBoot+Vue 可視化拖拽編輯的大屏項目(附源碼)

        轉(zhuǎn)發(fā)在看就是最大的支持??

        瀏覽 35
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 91蜜臀 | 插穴免费视频 | 四川老妇女乱hd | 色噜噜视频 | 日韩三级片免费 |