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

          宕機了,Redis數(shù)據(jù)丟了怎么辦?

          共 4646字,需瀏覽 10分鐘

           ·

          2021-01-04 21:08

          目錄
          • 前言
          • 什么是AOF?
            • 三種寫回策略
            • 日志文件太大怎么辦?
            • AOF重寫會阻塞主線程嗎?
            • AOF的缺點
            • 總結(jié)
          • 什么是RDB?
            • 給哪些數(shù)據(jù)做快照?
            • 快照時能夠修改數(shù)據(jù)嗎?
            • 多久做一次快照?
            • 增量快照
            • AOF和RDB混合使用
            • 總結(jié)
          • 總結(jié)


          前言

          Redis作為內(nèi)存型的數(shù)據(jù)庫,雖然很快,依然有著很大的隱患,一旦「服務(wù)器宕機」重啟,內(nèi)存中數(shù)據(jù)還會存在嗎?
          很容易想到的一個方案是從后臺數(shù)據(jù)恢復(fù)這些數(shù)據(jù),如果數(shù)據(jù)量很小,這倒是一個可行的方案。但是如果數(shù)據(jù)量過大,頻繁的從后臺數(shù)據(jù)庫訪問數(shù)據(jù),壓力很大;另外一方面恢復(fù)數(shù)據(jù)的時間極慢。
          對于Redis來說,實現(xiàn)數(shù)據(jù)的持久化和快速恢復(fù)是至關(guān)重要。
          今天這篇文章就來介紹一下Redis持久化的兩種機制AOF日志、RDB快照。

          ?

          什么是 AOF 日志?

          AOF(Append Only File)日志稱之為「寫后日志」,即是命令先執(zhí)行完成,把數(shù)據(jù)寫入內(nèi)存,然后才會記錄日志。
          AOF日志(文本形式)會將收到每一條的命令且執(zhí)行成功的命令以一定的格式寫入到文本中(追加的方式)。
          「寫后日志有什么好處呢?」 如下:
          1. 對于寫前日志無論命令是否執(zhí)行成功都會被記錄,但是Redis的寫后日志則只有命令執(zhí)行成功才會被寫入日志,避免了日志中存在錯誤命令;
          2. 同時由于是命令執(zhí)行成功之后才會寫入日志,因此不會阻塞當(dāng)前命令的執(zhí)行。
          但是AOF日志也有「潛在的風(fēng)險」,分析如下:
          1. 由于是寫后日志,如果在命令執(zhí)行成功之后,在日志未寫入磁盤之前服務(wù)器突然宕機,那重啟恢復(fù)數(shù)據(jù)的時候,這部分的數(shù)據(jù)肯定在日志文件中不存在了,那么將會丟失。(無法通過后臺數(shù)據(jù)庫恢復(fù)的情況下)
          2. 雖然不會阻塞當(dāng)前命令的執(zhí)行,由于記錄日志也是在主線程中(Redis是單線程),如果日志寫入磁盤的時候突然阻塞了,肯定會影響下一個命令的執(zhí)行。
          為了解決上面的風(fēng)險,AOF日志提供了三種回寫策略。

          三種寫回策略

          AOF機制提供了三種回寫策略,這些都在appendfsync配置,如下:
          1. Always(同步寫回):命令執(zhí)行完成,立馬同步的將日志寫入磁盤
          2. Everysec(每秒寫回):命令執(zhí)行完成后,先將日志寫入 AOF 文件的內(nèi)存緩沖區(qū),每隔一秒把緩沖區(qū)中內(nèi)容寫入磁盤。
          3. No(操作系統(tǒng)控制的寫回):每個寫命令執(zhí)行完,只是先把日志寫到AOF文件的內(nèi)存緩沖區(qū),由操作系統(tǒng)決定何時將緩沖區(qū)內(nèi)容寫回磁盤。
          其實這三中寫回策略都無法解決主線程的阻塞和數(shù)據(jù)丟失的問題,分析如下:
          1. 同步寫回:基本不丟失數(shù)據(jù),但是每步操作都會有一個慢速的落盤操作,不可避免的影響主線程性能。
          2. 每秒寫回:采用一秒寫一次到 AOF 日志文件中,但是一旦宕機還是會丟失一秒的數(shù)據(jù)。
          3. 操作系統(tǒng)控制的寫回:在寫完緩沖區(qū)之后則會寫入磁盤,但是數(shù)據(jù)始終在緩沖區(qū)的時間內(nèi)一旦宕機,數(shù)據(jù)還是會丟失。
          以上三種策略優(yōu)缺點總結(jié)如下表:

          日志文件太大怎么辦?

          隨著數(shù)據(jù)量的增大,AOF日志文件難免會很大,這樣將會導(dǎo)致寫入和恢復(fù)數(shù)據(jù)都將變得非常慢。此時AOF提供了一種「重寫機制」解決這一問題。
          ?重寫機制理解起來很簡單,即是Redis會創(chuàng)建一個新的AOF日志文件,將每個鍵值對最終的值用一條命令寫入日志文件中。
          比如讀取了鍵值對key1:value1,重寫機制會在新的AOF日志文件中記錄如下一條命令:
          set key1 value1
          其實即是記錄多次修改的最終的值記錄在新的AOF日志文件中,這樣當(dāng)恢復(fù)數(shù)據(jù)時可直接執(zhí)行該命令。
          「為什么重寫機制能夠縮小文件呢?」 當(dāng)一個鍵值被多次修改后,AOF日志文件中將會記錄多次修改鍵值的命令,重寫機制是根據(jù)這個鍵值最新狀態(tài)為它生成「寫入」命令,這樣舊文件中的「多條」命令在重寫后的新日志中變成了「一條」命令。
          作者畫了一張重寫流程圖,僅供參考,如下:
          重寫機制流程

          AOF重寫會阻塞主線程嗎?

          AOF重寫雖然能夠縮減日志文件的大小,達到減少日志記錄和數(shù)據(jù)恢復(fù)的時間,但是在數(shù)據(jù)量非常的大情況下把整個數(shù)據(jù)庫重寫后的日志寫入磁盤是一個非常耗時的過程,難道不會阻塞主線程嗎?
          「答案是:不會阻塞主線程」;因為AOF重寫過程是由后臺子進程bgrewriteaof來完成的,這也是為了避免阻塞主線程,導(dǎo)致數(shù)據(jù)庫性能下降。
          其實重寫的過程分為兩個階段:「一個拷貝,兩處日志」。
          「一個拷貝」:指每次執(zhí)行重寫時,主線程都fork一個子線程bgrewriteaof,主線程會把內(nèi)存數(shù)據(jù)拷貝一份到子線程,此時子線程中包含了數(shù)據(jù)庫的最新數(shù)據(jù)。然后子線程就能在不影響主線程的情況下進行AOF重寫了。
          「兩處日志」是什么?如下:
          1. 第一處日志:子線程重寫并未阻塞主線程,此時主線程仍然會處理請求,此時的AOF日志仍然正在記錄著,這樣即使宕機了,數(shù)據(jù)也是齊全的。第一處日志即是值主線程正在使用的日志。
          2. 第二處日志:指新的AOF重寫日志;重寫過程中的操作也會被寫到重寫日志緩沖區(qū),這樣重寫日志也不會丟失最新的操作。等到拷貝數(shù)據(jù)的所有操作記錄重寫完成后,重寫日志記錄的這些最新操作也會寫入新的 AOF 文件,以保證數(shù)據(jù)庫最新狀態(tài)的記錄。此時,我們就可以用新的 AOF 文件替代舊文件了。
          ?「總結(jié)」Redis在進行AOF重寫時,會fork一個子線程(不會阻塞主線程)并進行內(nèi)存拷貝用于重寫,然后使用兩個日志保證重寫過程中,新寫入的數(shù)據(jù)不會丟失。

          AOF的缺點

          雖說進行了日志重寫后,AOF日志文件會縮減很多,但是在數(shù)據(jù)恢復(fù)過程中仍然是一條命令一條命令(由于單線程,只能順序執(zhí)行)的執(zhí)行恢復(fù)數(shù)據(jù),這個恢復(fù)的過程非常緩慢。

          總結(jié)

          AOF這種通過逐一記錄操作命令的日志方式,提供了三種寫回策略保證數(shù)據(jù)的可靠性,分別是Always、EverysecNo,這三種策略在可靠性上是從高到低,而在性能上則是從低到高。
          為了避免日志文件過大,Redis提供了重寫的機制,每次重寫都fork一個子線程,拷貝內(nèi)存數(shù)據(jù)進行重寫,將多條命令縮減成一條生成鍵值對的命令,最終重寫的日志作為新的日志。


          什么是RDB?

          RDB(Redis DataBase)是另外一種持久化方式:內(nèi)存快照。
          ?RDB記錄的是「某一個時刻」的內(nèi)存數(shù)據(jù),并不是操作命令。
          這種方式類似于拍照,只保留某一時刻的形象。內(nèi)存快照是將某一時刻的狀態(tài)以文件的形式寫入磁盤。這樣即使宕機了,數(shù)據(jù)也不會丟失,這個快照文件就稱為RDB文件。
          ?由于記錄的是某個時刻的內(nèi)存數(shù)據(jù),數(shù)據(jù)恢復(fù)非??斓模恍枰馎OF日志逐一執(zhí)行記錄的命令。

          給哪些數(shù)據(jù)做快照?

          為了保證數(shù)據(jù)的可靠性,Redis執(zhí)行的「全量快照」,也就是把內(nèi)存中的所有數(shù)據(jù)都寫到磁盤中。
          隨著數(shù)據(jù)量的增大,一次性把全部數(shù)據(jù)都寫到磁盤中勢必會造成線程阻塞,這就關(guān)系到Redis的性能了。
          針對線程阻塞的問題Redis提供了兩個命令,如下:
          1. save:在主線程中執(zhí)行,會導(dǎo)致主線程阻塞。
          2. bgsavefork一個子進程,專門用于寫入RDB文件,避免了主線程的阻塞,這是Redis的默認(rèn)配置。
          這樣就可以使用bgsave命令執(zhí)行全量快照,既可以保證數(shù)據(jù)的可靠性也避免了主線程的阻塞。

          快照時能夠修改數(shù)據(jù)嗎?

          子線程執(zhí)行全量快照的同時,主線程仍然在接受著請求,讀數(shù)據(jù)肯定沒有問題,但是如果個修改了數(shù)據(jù),如何能夠保證快照的完整性呢?
          「舉個栗子」:我在T時刻進行全量快照,假設(shè)數(shù)據(jù)量有8G,寫入磁盤的過程至少需要20S,在這20S的時間內(nèi),一旦內(nèi)存中的數(shù)據(jù)發(fā)生了修改,則快照的完整性就破壞了。
          但是如果在快照時不能修改數(shù)據(jù),則對Redis的性能有巨大的影響,對于這個問題,Redis是如何解決的呢?
          ?Redis借助操作系統(tǒng)提供的寫時復(fù)制技術(shù)(Copy-On-Write, COW),在執(zhí)行快照的同時,正常處理寫操作。
          其實很簡單,bgsave命令會fork一個子線程,這個子線程共享所有內(nèi)存的數(shù)據(jù),子線程會讀取主線程內(nèi)存中的數(shù)據(jù),將他們寫入RDB文件。
          寫時復(fù)制保證數(shù)據(jù)可修改
          如上圖,對于鍵值對A的讀取并不會影響子線程,但是如果主線程一旦修改內(nèi)存中一塊數(shù)據(jù)(例如鍵值對D),這塊數(shù)據(jù)將會被復(fù)制一個副本,然后bgsave子線程會將其寫入RDB文件。

          多久做一次快照?

          快照只是記錄某一時刻的數(shù)據(jù),一旦時間隔離很久,則服務(wù)器一旦宕機,則會丟失那段時間的數(shù)據(jù)。
          比如在T1時間做了一次快照,在T1+t時又做了一次快照,如果在t這個時間段內(nèi)服務(wù)器突然宕機了,則快照中只保存了T1時刻的快照,在t時間段內(nèi)的數(shù)據(jù)修改未被記錄(丟失)。如下圖:
          t時刻宕機,未執(zhí)行快照
          從上圖明顯可以看出,RDB并不是一個完美的日志記錄方案」,只有讓t時間逐漸縮小,才能保證丟失的數(shù)據(jù)縮小。
          「那么問題來了,時間能夠縮短1秒嗎?」 即是每秒執(zhí)行一次快照。
          ?全量快照是記錄某一個時刻的「全部」內(nèi)存數(shù)據(jù),每秒執(zhí)行一次的對Redis性能影響巨大,于是「增量快照」就出來了。

          增量快照

          「增量快照是指做了一次全量快照之后,后續(xù)的快照只對修改的數(shù)據(jù)進行快照記錄」,這樣可以避免每次都全量快照的開銷。
          增量快照的前提是Redis能夠記住修改的數(shù)據(jù),這個功能其實開銷也是巨大的,需要保存完整的鍵值對,這對內(nèi)存的消耗是巨大的。
          ?為了解決這個問題,Redis使用了AOFRDB混合使用的方式。

          AOF和RDB混合使用

          這個概念是在Redis4.0提出的,簡單的說就是「內(nèi)存快照以一定的頻率執(zhí)行,比如1小時一次,在兩次快照之間,使用AOF日志記錄這期間的所有命令操作。」
          ?混合使用的方式使得內(nèi)存快照不必頻繁的執(zhí)行,并且AOF記錄的也不是全部的操作命令,而是兩次快照之間的操作命令,不會出現(xiàn)AOF日志文件過大的情況了,避免了AOF重寫的開銷了。
          這個方案既能夠用到的RDB的快速恢復(fù)的好處,又能享受都只記錄操作命令的簡單優(yōu)勢,強烈建議使用。

          總結(jié)

          RDB內(nèi)存快照記錄的是某一個時刻的內(nèi)存數(shù)據(jù),因此能夠快速恢復(fù);AOFRDB混合使用能夠使得宕機后數(shù)據(jù)快速恢復(fù),又能夠避免AOF日志文件過大。

          ?

          總結(jié)

          本文介紹了兩種數(shù)據(jù)恢復(fù)和持久化的方案,分別是AOFRDB。
          AOF介紹了什么?如下:
          1. AOF是寫后日志,通過記錄操作命令持久化數(shù)據(jù)。
          2. 由于AOF是在命令執(zhí)行之后記錄日志,如果在寫入磁盤之前服務(wù)器宕機,則會丟失數(shù)據(jù);如果寫入磁盤的時候突然阻塞,則會阻塞主線程;為了解決以上問題,AOF機制提供了三種寫回的策略,每種策略都有不同的優(yōu)缺點。
          3. AOF日志文件過大怎么辦?AOF通過fork一個子線程重寫一個新的日志文件(共享主線程的內(nèi)存,記錄最新數(shù)據(jù)的寫入命令),同時子線程重寫,避免阻塞主線程。
          RDB介紹了什么?如下:
          1. RDB是內(nèi)存快照,記錄某一個時刻的內(nèi)存數(shù)據(jù),而不是操作命令。
          2. Redis提供了兩個命令,分別是save、bgsave來執(zhí)行全量快照,這兩個命令的區(qū)別則是save是在主線程執(zhí)行,勢必會阻塞主線程,bgsave是在fork一個子線程,共享內(nèi)存。
          3. RDB通過操作系統(tǒng)的「寫時復(fù)制技術(shù)」,能夠保證在執(zhí)行快照的同時主線程能夠修改快照。
          4. 由于兩次快照之間是存在間隔的,一旦服務(wù)器宕機,則會丟失兩次間隔時刻的數(shù)據(jù),Redis4.0開始使用AOF日志記錄兩次快照之間執(zhí)行的命令(AOFRDB混合使用)。

          有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)

          歡迎大家關(guān)注Java之道公眾號


          好文章,我在看??

          瀏覽 40
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  欧美在线视频导航 | 三级黄色国产高清电影 | 青青操青青操在线视频免费 | 人人操,人人色 | 99久久久无码国产精精品品不卡 |