<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>

          你管這破玩意叫 RDB

          共 2964字,需瀏覽 6分鐘

           ·

          2021-05-09 11:09

          低并發(fā)編程
          戰(zhàn)略上藐視技術(shù),戰(zhàn)術(shù)上重視技術(shù)

          本文末有小驚喜


          我是個(gè) redis 服務(wù),我馬上就要掛了

          我已經(jīng)運(yùn)行了好幾年了,我的內(nèi)存中存儲(chǔ)著好多鍵值對(duì)。

          如果我掛了,那樣我內(nèi)存中的數(shù)據(jù)就全沒(méi)了。
          我得想個(gè)辦法,時(shí)不時(shí)把數(shù)據(jù)復(fù)制到硬盤上保存起來(lái)。
          我把這個(gè)偉大的計(jì)劃,稱為持久化計(jì)劃。

          停下手頭的工作


          我首先想到的最簡(jiǎn)單的辦法,就是先拒絕新來(lái)的命令,開始將內(nèi)存中的數(shù)據(jù)復(fù)制到硬盤。
          等拷貝完成后,再開始接受新命令。

          這樣可以保證我拷貝的時(shí)候,沒(méi)有新來(lái)的命令修改內(nèi)存,也即保證了時(shí)點(diǎn)性。
          簡(jiǎn)單說(shuō)就是,我保存的是某一時(shí)刻的 Redis 內(nèi)存狀態(tài)。
          但這樣每次持久化都要阻塞客戶端命令,肯定要被罵。

          不停止手頭的工作


          這好辦,那我就不停止手頭的工作唄,一邊接受命令,一邊做持久化,如下。

          ?這樣效率提高了不少,持久化不再阻塞客戶端執(zhí)行命令了。
          但是,你有沒(méi)有注意到,內(nèi)存中,某一時(shí)刻的數(shù)據(jù),只有三種情況:
          閃客 18 低并發(fā)編程
          低兄 18 低并發(fā)編程
          低兄 18 求三連
          而此時(shí)硬盤中持久化的數(shù)據(jù)是:
          閃客 18 求三連
          它無(wú)法表示任何一個(gè)時(shí)刻的內(nèi)存數(shù)據(jù)。
          那這樣的快照就失去了意義,也即沒(méi)有保證時(shí)點(diǎn)性。
          這顯然也是不行的。

          先復(fù)制一份內(nèi)存


          這可咋辦呢?
          停止手頭的工作可以保證時(shí)點(diǎn)性,但阻塞了客戶端。
          不停止手頭的工作,雖然不阻塞客戶端,但又無(wú)法保證時(shí)點(diǎn)性。
          真令人頭大啊。
          抓了一會(huì)頭皮后,我冷靜了下來(lái),開始分析。
          時(shí)點(diǎn)性是必須保證的,否則快照就沒(méi)有了意義,那就只能嘗試將阻塞客戶端的時(shí)間變短一點(diǎn)了。
          之前的阻塞客戶端時(shí)間,是消耗在持久化,也就是內(nèi)存拷貝到硬盤這個(gè)過(guò)程。
          優(yōu)化一下,先從內(nèi)存中拷貝一份到另一塊內(nèi)存空間,然后再對(duì)這塊新的內(nèi)存空間進(jìn)行持久化。

          ?這樣,持久化的過(guò)程不耽誤客戶端命令,同時(shí)不受客戶端命令影響,保證了時(shí)點(diǎn)性。
          而阻塞客戶端的時(shí)間,僅僅是內(nèi)存與內(nèi)存之間拷貝一份數(shù)據(jù)的時(shí)間,相比于整個(gè)持久化過(guò)程,可以忽略不計(jì)。
          完美!
          帶著這套完美的方案,我去找我的主人邀功了。

          寫時(shí)復(fù)制


          我:主人,我做好持久化方案啦!
          主人:嗯我看看... 哎呀,把內(nèi)存復(fù)制一份,這個(gè)想法很好,但是差了點(diǎn)火候呀,你對(duì)操作系統(tǒng)了解的還不夠深入。
          我:啊,為啥呢?
          主人:你想想看,你現(xiàn)在的目的,就是為了讓持久化和處理客戶端命令的這兩個(gè)過(guò)程所用到的內(nèi)存空間隔離開,是不是?
          我:嗯嗯是的。
          主人:對(duì)呀,那其實(shí)你只需要新建一個(gè)進(jìn)程去做持久化的過(guò)程即可,不同進(jìn)程之間的內(nèi)存是隔離的,也就是新建一個(gè)進(jìn)程,會(huì)將原有進(jìn)程的內(nèi)存空間完全拷貝一份新的。

          ?我:啊,那這不是和我自己復(fù)制一份內(nèi)存一樣嘛,耗時(shí)差不多吧?
          主人:我剛剛的圖只是給用戶的感覺(jué)是這樣的,實(shí)際上,linux 采用了寫時(shí)復(fù)制技術(shù),在 fork 出子進(jìn)程時(shí)并沒(méi)有立刻將內(nèi)存進(jìn)行拷貝,僅僅是拷貝了一份映射關(guān)系,讓它們暫時(shí)指向同一個(gè)內(nèi)存空間。

          ?主人:而當(dāng)父子進(jìn)程對(duì)這塊內(nèi)存空間進(jìn)行寫操作時(shí),才會(huì)真正復(fù)制內(nèi)存,而且是以頁(yè)為單位。
          我:原來(lái)如此,也就是說(shuō),我可以利用操作系統(tǒng)的進(jìn)程的寫時(shí)復(fù)制內(nèi)存的原理,來(lái)代替我自己復(fù)制全部?jī)?nèi)存這個(gè)方案,因?yàn)槌志没^(guò)程,對(duì)內(nèi)存的寫操作想來(lái)也不會(huì)特別多,大多數(shù)值都是不變的,所以這樣就提高了效率。
          主人:是的,正是如此。
          我:妙呀!
          我趕緊把方案修改了,要持久化時(shí)我就 fork 一個(gè)子進(jìn)程去做這件事,由操作系統(tǒng)的進(jìn)程內(nèi)存隔離的特征替我保證時(shí)點(diǎn)性,寫時(shí)復(fù)制原理替我保證效率,也就是減少客戶端阻塞時(shí)間,偽代碼大概是這個(gè)樣子。
          void rdbSaveBackground() {
              // 子進(jìn)程處理(利用了操作系統(tǒng)的寫時(shí)復(fù)制技術(shù))
              if ((childpid = fork()) == 0) {
                  // 落盤主方法
                  rdbSave();
              }
          }
          完美!

          還沒(méi)定結(jié)構(gòu)呢


          剛剛光顧著想持久化的過(guò)程了,還沒(méi)定寫到磁盤中的數(shù)據(jù)格式呢。
          那就定一個(gè)唄。
          假如我的 Redis 內(nèi)存只有一條數(shù)據(jù),是通過(guò)下面的命令寫入的:
          set dibingfa niubi
          那持久化后落到磁盤中的 rdb 文件將會(huì)是這個(gè)樣子。

          ?好了,大功告成,我再也不用擔(dān)心自己掛了,會(huì)有人幫我從持久化文件中恢復(fù)我的內(nèi)存數(shù)據(jù)的。
          但沒(méi)來(lái)得及持久化的,我就不管了。
          具體什么時(shí)候進(jìn)行一次持久化,我給主人留了一個(gè)配置
          save m n
          表示 m 秒內(nèi)數(shù)據(jù)集存在 n 次修改時(shí),自動(dòng)觸發(fā)一次持久化。
          主人也可以配置多個(gè)這樣的配置項(xiàng)。
          而我也好心給主人配了個(gè)默認(rèn)的配置項(xiàng),并寫了段注釋。

          # Save the DB on disk:
          #   In the example below the behaviour will be to save:
          #   after 900 sec (15 min) if at least 1 key changed
          #   after 300 sec (5 min) if at least 10 keys changed
          #   after 60 sec if at least 10000 keys changed

          save 900 1
          save 300 10
          save 60 10000

          我想以主人的英文水平,應(yīng)該可以讀得懂。
          好啦,這回真的是大功告成了!
          這個(gè)破玩意,我給起個(gè)名字,就叫 RDB。
          沒(méi)什么特別的含義,其實(shí)就是用我的名字作為開頭,Redis DB 而已。

          后記





          rdb 持久化過(guò)程,也可以手動(dòng)觸發(fā),即直接輸入 bgsave,同自動(dòng)觸發(fā)完全一樣。

          其在 redis 的源碼中,叫做 bgsaveCommand 方法。

          整個(gè)源碼非常簡(jiǎn)單易讀,只是干擾項(xiàng)很多罷了。

          我將干擾項(xiàng)全部去掉,整理了一份 RDB 源碼簡(jiǎn)潔版,并配上了我魔性的注釋,制作了一份精致的小 pdf。

          本來(lái)想著讓大家分享到朋友圈獲取,但想想看算了,加我好友公眾號(hào)低并發(fā)編程-菜單欄-關(guān)于,即可獲取這個(gè)精致的小 PDF。

          瀏覽 25
          點(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>
                  A一区二区免费视频 | 噜噜色av| 成人性生交大免费看 | 加勒比色综合 | 无码在线免费看 |