1. 程式人生 > >redis持久化機制

redis持久化機制

toolbar 我們 等等 HERE 了解 OS row alt 數據

redis持久化機制

參考:

https://www.cnblogs.com/xingzc/p/5988080.html

https://blog.csdn.net/yujin2010good/article/details/54729818

Redis提供的持久化機制 

  Redis是一種面向“key-value”類型數據的分布式NoSQL數據庫系統,具有高性能、持久存儲、適應高並發應用場景等優勢。它雖然起步較晚,但發展卻十分迅速。

近日,Redis的作者在博客中寫到,他看到的所有針對Redis的討論中,對Redis持久化的誤解是最大的,於是他寫了一篇長文來對Redis的持久化進行了系統性的論述。

文章主要包含三個方面:Redis持久化是如何工作的、這一性能是否可靠以及和其它類型的數據庫比較

。以下為文章內容:

一、Redis持久化是如何工作的?

  什麽是持久化?簡單來講就是將數據放到斷電後數據不會丟失的設備中,也就是我們通常理解的硬盤上。

首先我們來看一下數據庫在進行寫操作時到底做了哪些事,主要有下面五個過程

  • 客戶端向服務端發送寫操作(數據在客戶端的內存中)。
  • 數據庫服務端接收到寫請求的數據(數據在服務端的內存中)。
  • 服務端調用write這個系統調用,將數據往磁盤上寫(數據在系統內存的緩沖區中)。
  • 操作系統將緩沖區中的數據轉移到磁盤控制器上(數據在磁盤緩存中)。
  • 磁盤控制器將數據寫到磁盤的物理介質中(數據真正落到磁盤上)。

故障分析

寫操作大致有上面5個流程,下面我們結合上面的5個流程看一下各種級別的故障

  • 當數據庫系統故障時,這時候系統內核還是完好的。那麽此時只要我們執行完了第3步,那麽數據就是安全的,因為後續操作系統會來完成後面幾步,保證數據最終會落到磁盤上。
  • 當系統斷電時,這時候上面5項中提到的所有緩存都會失效,並且數據庫和操作系統都會停止工作。所以只有當數據在完成第5步後,才能保證在斷電後數據不丟失

通過上面5步的了解,可能我們會希望搞清下面一些問題

  • 數據庫多長時間調用一次write,將數據寫到內核緩沖區?
  • 內核多長時間會將系統緩沖區中的數據寫到磁盤控制器?
  • 磁盤控制器又在什麽時候把緩存中的數據寫到物理介質上?

  對於第一個問題,通常數據庫層面會進行全面控制。

  而對第二個問題,操作系統有其默認的策略,但是我們也可以通過POSIX API提供的fsync系列命令強制操作系統將數據從內核區寫到磁盤控制器上。

  對於第三個問題,好像數據庫已經無法觸及,但實際上,大多數情況下磁盤緩存是被設置關閉的,或者是只開啟為讀緩存,也就是說寫操作不會進行緩存,直接寫到磁盤。

  建議的做法是僅僅當你的磁盤設備有備用電池時才開啟寫緩存

數據損壞

  所謂數據損壞,就是數據無法恢復,上面我們講的都是如何保證數據是確實寫到磁盤上去,但是寫到磁盤上可能並不意味著數據不會損壞。比如我們可能一次寫請求會進行兩次不同的寫操作,當意外發生時,可能會導致一次寫操作安全完成,但是另一次還沒有進行。如果數據庫的數據文件結構組織不合理,可能就會導致數據完全不能恢復的狀況出現。

這裏通常也有三種策略來組織數據,以防止數據文件損壞到無法恢復的情況

    • 第一種是最粗糙的處理,就是不通過數據的組織形式保證數據的可恢復性。而是通過配置數據同步備份的方式,在數據文件損壞後通過數據備份來進行恢復。實際上MongoDB在不開啟操作日誌,通過配置Replica Sets時就是這種情況。
    • 另一種是在上面基礎上添加一個操作日誌,每次操作時記一下操作的行為,這樣我們可以通過操作日誌來進行數據恢復。因為操作日誌是順序追加的方式寫的,所以不會出現操作日誌也無法恢復的情況。這也類似於MongoDB開啟了操作日誌的情況。
    • 更保險的做法是數據庫不進行舊數據的修改,只是以追加方式去完成寫操作,這樣數據本身就是一份日誌,這樣就永遠不會出現數據無法恢復的情況了。實際上CouchDB就是此做法的優秀範例。

 

二 、Redis提供了RDB持久化和AOF持久化

RDB機制的優勢和略施

  RDB持久化是指在指定的時間間隔內將內存中的數據集快照寫入磁盤。

  也是默認的持久化方式,這種方式是就是將內存中數據以快照的方式寫入到二進制文件中,默認的文件名為dump.rdb。

可以通過配置設置自動做快照持久化的方式。我們可以配置redis在n秒內如果超過m個key被修改就自動做快照,下面是默認的快照保存配置

   save 900 1     #900秒內如果超過1個key被修改,則發起快照保存
   save 300 10    #300秒內容如超過10個key被修改,則發起快照保存
   save 60 10000

RDB文件保存過程

  • redis調用fork,現在有了子進程和父進程。
  • 父進程繼續處理client請求,子進程負責將內存內容寫入到臨時文件。由於os的寫時復制機制(copy on write)父子進程會共享相同的物理頁面,當父進程處理寫請求時os會為父進程要修改的頁面創建副本,而不是寫共享的頁面。所以子進程的地址空間內的數 據是fork時刻整個數據庫的一個快照。
  • 當子進程將快照寫入臨時文件完畢後,用臨時文件替換原來的快照文件,然後子進程退出。

client 也可以使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主線程中保存快照的,由於redis是用一個主線程來處理所有 client的請求,這種方式會阻塞所有client請求。所以不推薦使用。

另一點需要註意的是,每次快照持久化都是將內存數據完整寫入到磁盤一次,並不 是增量的只同步臟數據。如果數據量大的話,而且寫操作比較多,必然會引起大量的磁盤io操作,可能會嚴重影響性能。

優勢

  • 一旦采用該方式,那麽你的整個Redis數據庫將只包含一個文件,這樣非常方便進行備份。比如你可能打算沒1天歸檔一些數據。
  • 方便備份,我們可以很容易的將一個一個RDB文件移動到其他的存儲介質上
  • RDB 在恢復大數據集時的速度比 AOF 的恢復速度要快。
  • RDB 可以最大化 Redis 的性能:父進程在保存 RDB 文件時唯一要做的就是 fork 出一個子進程,然後這個子進程就會處理接下來的所有保存工作,父進程無須執行任何磁盤 I/O 操作。

劣勢

  • 如果你需要盡量避免在服務器故障時丟失數據,那麽 RDB 不適合你。 雖然 Redis 允許你設置不同的保存點(save point)來控制保存 RDB 文件的頻率, 但是, 因為RDB 文件需要保存整個數據集的狀態, 所以它並不是一個輕松的操作。 因此你可能會至少 5 分鐘才保存一次 RDB 文件。 在這種情況下, 一旦發生故障停機, 你就可能會丟失好幾分鐘的數據。
  • 每次保存 RDB 的時候,Redis 都要 fork() 出一個子進程,並由子進程來進行實際的持久化工作。 在數據集比較龐大時, fork() 可能會非常耗時,造成服務器在某某毫秒內停止處理客戶端; 如果數據集非常巨大,並且 CPU 時間非常緊張的話,那麽這種停止時間甚至可能會長達整整一秒。 雖然 AOF 重寫也需要進行 fork() ,但無論 AOF 重寫的執行間隔有多長,數據的耐久性都不會有任何損失。

AOF文件保存過程

redis會將每一個收到的寫命令都通過write函數追加到文件中(默認是 appendonly.aof)。

當redis重啟時會通過重新執行文件中保存的寫命令來在內存中重建整個數據庫的內容。當然由於os會在內核中緩存 write做的修改,所以可能不是立即寫到磁盤上。這樣aof方式的持久化也還是有可能會丟失部分修改。不過我們可以通過配置文件告訴redis我們想要 通過fsync函數強制os寫入到磁盤的時機。有三種方式如下(默認是:每秒fsync一次)

appendonly yes              //啟用aof持久化方式
# appendfsync always      //每次收到寫命令就立即強制寫入磁盤,最慢的,但是保證完全的持久化,不推薦使用
appendfsync everysec     //每秒鐘強制寫入磁盤一次,在性能和持久化方面做了很好的折中,推薦
# appendfsync no    //完全依賴os,性能最好,持久化沒保證

aof 的方式也同時帶來了另一個問題。持久化文件會變的越來越大。例如我們調用incr test命令100次,文件中必須保存全部的100條命令,其實有99條都是多余的。因為要恢復數據庫的狀態其實文件中保存一條set test 100就夠了。

為了壓縮aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis將使用與快照類似的方式將內存中的數據 以命令的方式保存到臨時文件中,最後替換原來的文件。具體過程如下

  • redis調用fork ,現在有父子兩個進程
  • 子進程根據內存中的數據庫快照,往臨時文件中寫入重建數據庫狀態的命令
  • 父進程繼續處理client請求,除了把寫命令寫入到原來的aof文件中。同時把收到的寫命令緩存起來。這樣就能保證如果子進程重寫失敗的話並不會出問題。
  • 當子進程把快照內容寫入已命令方式寫到臨時文件中後,子進程發信號通知父進程。然後父進程把緩存的寫命令也寫入到臨時文件。
  • 現在父進程可以使用臨時文件替換老的aof文件,並重命名,後面收到的寫命令也開始往新的aof文件中追加。

需要註意到是重寫aof文件的操作,並沒有讀取舊的aof文件,而是將整個內存中的數據庫內容用命令的方式重寫了一個新的aof文件,這點和快照有點類似。

優勢

  • 使用 AOF 持久化會讓 Redis 變得非常耐久(much more durable):你可以設置不同的 fsync 策略,比如無 fsync ,每秒鐘一次 fsync ,或者每次執行寫入命令時 fsync 。 AOF 的默認策略為每秒鐘 fsync 一次,在這種配置下,Redis 仍然可以保持良好的性能,並且就算發生故障停機,也最多只會丟失一秒鐘的數據( fsync 會在後臺線程執行,所以主線程可以繼續努力地處理命令請求)。

  • AOF 文件是一個只進行追加操作的日誌文件(append only log), 因此對 AOF 文件的寫入不需要進行 seek , 即使日誌因為某些原因而包含了未寫入完整的命令(比如寫入時磁盤已滿,寫入中途停機,等等), redis-check-aof 工具也可以輕易地修復這種問題。
    Redis 可以在 AOF 文件體積變得過大時,自動地在後臺對 AOF 進行重寫: 重寫後的新 AOF 文件包含了恢復當前數據集所需的最小命令集合。 整個重寫操作是絕對安全的,因為 Redis 在創建新 AOF 文件的過程中,會繼續將命令追加到現有的 AOF 文件裏面,即使重寫過程中發生停機,現有的 AOF 文件也不會丟失。 而一旦新 AOF 文件創建完畢,Redis 就會從舊 AOF 文件切換到新 AOF 文件,並開始對新 AOF 文件進行追加操作。

  • AOF 文件有序地保存了對數據庫執行的所有寫入操作, 這些寫入操作以 Redis 協議的格式保存, 因此 AOF 文件的內容非常容易被人讀懂, 對文件進行分析(parse)也很輕松。 導出(export) AOF 文件也非常簡單: 舉個例子, 如果你不小心執行了 FLUSHALL 命令, 但只要 AOF 文件未被重寫, 那麽只要停止服務器, 移除 AOF 文件末尾的 FLUSHALL 命令, 並重啟 Redis , 就可以將數據集恢復到 FLUSHALL 執行之前的狀態。

劣勢

  • 對於相同的數據集來說,AOF 文件的體積通常要大於 RDB 文件的體積。

  • 根據所使用的 fsync 策略,AOF 的速度可能會慢於 RDB 。 在一般情況下, 每秒 fsync 的性能依然非常高, 而關閉 fsync 可以讓 AOF 的速度和 RDB 一樣快, 即使在高負荷之下也是如此。 不過在處理巨大的寫入載入時,RDB 可以提供更有保證的最大延遲時間(latency)。

  • AOF 在過去曾經發生過這樣的 bug : 因為個別命令的原因,導致 AOF 文件在重新載入時,無法將數據集恢復成保存時的原樣。 (舉個例子,阻塞命令 BRPOPLPUSH 就曾經引起過這樣的 bug 。) 測試套件裏為這種情況添加了測試: 它們會自動生成隨機的、復雜的數據集, 並通過重新載入這些數據來確保一切正常。 雖然這種 bug 在 AOF 文件中並不常見, 但是對比來說, RDB 幾乎是不可能出現這種 bug 的。

抉擇

一般來說, 如果想達到足以媲美 PostgreSQL 的數據安全性, 你應該同時使用兩種持久化功能。

如果你非常關心你的數據, 但仍然可以承受數分鐘以內的數據丟失, 那麽你可以只使用 RDB 持久化。

其余情況我個人喜好選擇AOF

1. Snapshotting:
缺省情況下,Redis會將數據集的快照dump到dump.rdb文件中。此外,我們也可以通過配置文件來修改Redis服務器dump快照的頻率,在打開6379.conf文件之後,我們搜索save,可以看到下面的配置信息:
save 900 1 #在900秒(15分鐘)之後,如果至少有1個key發生變化,則dump內存快照。
save 300 10 #在300秒(5分鐘)之後,如果至少有10個key發生變化,則dump內存快照。
save 60 10000 #在60秒(1分鐘)之後,如果至少有10000個key發生變化,則dump內存快照。

2. Dump快照的機制:
1). Redis先fork子進程。
2). 子進程將快照數據寫入到臨時RDB文件中。
3). 當子進程完成數據寫入操作後,再用臨時文件替換老的文件。

5.4.3. AOF文件:
上面已經多次講過,RDB的快照定時dump機制無法保證很好的數據持久性。如果我們的應用確實非常關註此點,我們可以考慮使用Redis中的AOF機制。對於Redis服務器而言,其缺省的機制是RDB,如果需要使用AOF,則需要修改配置文件中的以下條目:
將appendonly no改為appendonly yes
從現在起,Redis在每一次接收到數據修改的命令之後,都會將其追加到AOF文件中。在Redis下一次重新啟動時,需要加載AOF文件中的信息來構建最新的數據到內存中。

5.4.5. AOF的配置:
在Redis的配置文件中存在三種同步方式,它們分別是:
appendfsync always #每次有數據修改發生時都會寫入AOF文件。
appendfsync everysec #每秒鐘同步一次,該策略為AOF的缺省策略。
appendfsync no #從不同步。高效但是數據不會被持久化。

5.4.6. 如何修復壞損的AOF文件:
1). 將現有已經壞損的AOF文件額外拷貝出來一份。
2). 執行"redis-check-aof --fix <filename>"命令來修復壞損的AOF文件。
3). 用修復後的AOF文件重新啟動Redis服務器。

5.4.7. Redis的數據備份:
在Redis中我們可以通過copy的方式在線備份正在運行的Redis數據文件。這是因為RDB文件一旦被生成之後就不會再被修改。Redis每次都是將最新的數據dump到一個臨時文件中,之後在利用rename函數原子性的將臨時文件改名為原有的數據文件名。因此我們可以說,在任意時刻copy數據文件都是安全的和一致的。鑒於此,我們就可以通過創建cron job的方式定時備份Redis的數據文件,並將備份文件copy到安全的磁盤介質中。

5.5、立即寫入

技術分享圖片 技術分享圖片
//立即保存,同步保存
    public static void syncSave() throws Exception{
        Jedis jedis=new Jedis("127.0.0.1",6379);
        for (int i = 0; i <1000; i++) {
            jedis.set("key"+i, "Hello"+i);
            System.out.println("設置key"+i+"的數據到redis");
            Thread.sleep(2);
        }
        //執行保存,會在服務器下生成一個dump.rdb數據庫文件
        jedis.save();
        jedis.close();
        System.out.println("寫入完成");
    }
技術分享圖片 技術分享圖片

運行結果:

技術分享圖片

這裏的save方法是同步的,沒有寫入完成前不執行後面的代碼。

5.6、異步寫入

技術分享圖片 技術分享圖片
    //異步保存
    public static void asyncSave() throws Exception{
        Jedis jedis=new Jedis("127.0.0.1",6379);
        for (int i = 0; i <1000; i++) {
            jedis.set("key"+i, "Hello"+i);
            System.out.println("設置key"+i+"的數據到redis");
            Thread.sleep(2);
        }
        //執行異步保存,會在服務器下生成一個dump.rdb數據庫文件
        jedis.bgsave();
        jedis.close();
        System.out.println("寫入完成");
    }
技術分享圖片 技術分享圖片

如果數據量非常大,要保存的內容很多,建議使用bgsave,如果內容少則可以使用save方法。

為什麽Redis進行RDB持久化數據時,新起一個進程而不是在原進程中起一個線程來持久化數據:主要是出於Redis性能的考慮,(1)Redis RDB持久化機制會阻塞主進程,這樣主進程就無法響應客戶端請求。(2)我們知道Redis對客戶端響應請求的工作模型是單進程和單線程的,如果在主進程內啟動一個線程,這樣會造成對數據的競爭條件,為了避免使用鎖降低性能。基於以上兩點這就是為什麽Redis通過啟動一個進程來執行RDB了。

redis持久化原理詳解

一、 Redis 提供了不同級別的持久化方式:

Redis提供了兩種方式對數據進行持久化,分別是RDB和AOF。
RDB持久化方式能夠在指定的時間間隔能對你的數據進行快照存儲。
AOF持久化方式記錄每次對服務器寫的操作,當服務器重啟的時候會重新執行這些命令來恢復原始的數據,AOF命令以redis協議追加保存每次寫的操作到文件末尾。Redis還能對AOF文件進行後臺重寫,使得AOF文件的體積不至於過大。
如果你只希望你的數據在服務器運行的時候存在,你也可以不使用任何持久化方式。
你也可以同時開啟兩種持久化方式,,在這種情況下,當redis重啟的時候會優先載入AOF文件來恢復原始的數據,因為在通常情況下AOF文件保存的數據集要比RDB文件保存的數據集要完整。

二、 配置文件中對兩種存儲方式的設置

Redis默認開啟RDB的存儲方式。
The filename where to dump the DB
dbfilename “dump.rdb”
對於AOF的存儲方式redis並沒有默認開啟。通過配置開啟如下:
技術分享圖片
把註釋去掉就開啟了AOF的存儲方式。

三、 RDB(Redis DataBase)介紹

開啟RDB方式redis會在指定的時間段內將內存中的數據快照到磁盤中,redis啟動時再恢復到內存中。
Redis會單獨創建(fork)一個線程,將數據寫入到臨時文件中,持久化的過程都結束了,在用這個臨時文件替換上次的臨時文件。
如果需要進行大規模的數據恢復,並且對於數據恢復不是很敏感,RDB的方式比AOF方式更加高效,RDB的缺點就在於最後一次持久化後的數據有可能會丟失。
RDB持久化數據觸發配置在redis.conf中:
技術分享圖片
默認是當一條數據寫入時15分鐘持久化一次,當10條數據發生變化5分鐘(為了測試方便改成了2分鐘)持久化一次,當10000條數據發生變化1分鐘進行持久化。
RDB存儲方式測試:
技術分享圖片
兩分鐘後在文件夾中生成了一個dump.rdb的文件,這個就是臨時文件,保存該臨時文件。
技術分享圖片
再次清空數據庫:
技術分享圖片
然後刪除dum.rdb文件,將dump.rdb.bk文件恢復成dump.rdb。再啟動服務器。
技術分享圖片
如上圖所示,redis中的數據已經從dump.rdb中恢復過來了。

四、 AOF(APPEND ONLY FILE)存儲介紹

以日誌的形式來記錄每個寫操作,將Redis執行過的所有寫指令記錄下來(讀操作不記錄),
只許追加文件但不可以改寫文件,redis啟動之初會讀取該文件重新構建數據,換言之,redis
重啟的話就根據日誌文件的內容將寫指令從前到後執行一次以完成數據的恢復工作。
註:所有的指令記錄也包括flushDB操作,後面會有坑。
在redis中這種存儲方式默認是關閉的,需要在redis.conf文件中開啟,開啟方式在文中已經做了介紹,就不在贅述。
Redis對於AOF存儲方式是怎麽持久化的在redis.conf也有,如下:
技術分享圖片
配置文件對於這種方式的持久化有三種方式:
1、 有寫操作就寫。顯然這種方式影響性能。但是數據完整,不會丟數據
2、 不開啟。不開啟AOF就沒意思了
3、 每秒寫文件。折中的方式更加合適。但是有可能導致一秒的數據丟失。

AOF的重寫(Rewrite)

AOF采用文件追加方式,文件會越來越大為避免出現此種情況,新增了重寫機制,
當AOF文件的大小超過所設定的閾值時,Redis就會啟動AOF文件的內容壓縮。

重寫原理

AOF文件持續增長而過大時,會fork出一條新進程來將文件重寫(也是先寫臨時文件最後再rename),遍歷新進程的內存中數據,每條記錄有一條的Set語句。重寫aof文件的操作,並沒有讀取舊的aof文件,而是將整個內存中的數據庫內容用命令的方式重寫了一個新的aof文件,這點和快照有點類似。

觸發機制:

Redis會記錄上次重寫時的AOF大小,默認配置是當AOF文件大小是上次rewrite後大小的一倍且文件大於64M時觸發。
技術分享圖片

AOF存儲方式優點:

1、 每秒同步。
2、 每修改同步。

AOF存儲方式缺點:

1、 AOF文件遠大於EDB。
2、 運行效率慢。

AOF存儲方式測試:

1、寫入數據
技術分享圖片
3、 寫入之後在文件夾中出現了AOF文件,再對這個文件進行備份
技術分享圖片
4、 清空數據庫並退出
技術分享圖片
5、 恢復appendonly.aof文件
技術分享圖片
6、 啟動redis服務器,查看數據
技術分享圖片
如圖所示數據已經恢復。

五、 同時開啟了RDB和AOF兩種方式默認是哪種方式?

從剛才測試AOF可以看出兩種方式同時開啟是使用AOF的存儲方式。
技術分享圖片
當只開啟了RBD方式時數據庫中有10條數據,當開啟了AOF方式之後,由於appendonly.aof文件中沒有備份數據,所以啟動後如第二個框中框出的所示沒有數據。從這裏可以看出默認首先使用AOF的存儲方式。

六、 小結

1、 同時開啟兩種方式優先使用AOF方式。
2、 一般來說, 如果想達到足以媲美 PostgreSQL 的數據安全性, 你應該同時使用兩種持久化功能。
3、 如果你非常關心你的數據, 但仍然可以承受數分鐘以內的數據丟失, 那麽你可以只使用 RDB 持久化。
4、 有很多用戶都只使用 AOF 持久化, 但我們並不推薦這種方式: 因為定時生成 RDB 快照(snapshot)非常便於進行數據庫備份, 並且 RDB 恢復數據集的速度也要比 AOF 恢復的速度要快, 除此之外, 使用 RDB 還可以避免之前提到的 AOF 程序的 bug 。

redis持久化機制