Redis持久化原理隨想

Redis持久化

   衆所周知,Redis是內存數據庫,且使用單個線程來處理命令請求。它將本身的數據庫狀態(非空數據庫以及它們的鍵值對)存儲在內存裏面。因此若是沒有持久化機制,不把數據保存到硬盤裏面,那麼一旦服務器進程退出,服務器中的數據庫狀態也會消失不見。

爲了解決這個問題,redis 提供兩種方法進行數據持久化,分別是RDB和AOF。RDB能夠將Redis在內存中的數據庫狀態保存到磁盤裏面以實現持久化,AOF經過記錄寫命令達到持久化效果。兩種方法都有各自的優勢,須要咱們在生產環境中依照實際的業務狀況進行裁定。服務器按照下面的流程圖選擇持久化方式:

Redis  流程web

RDB 持久化

RDB持久化既能夠手動執行,也能夠依據配置文件選項按期執行。RDB持久化生成的RDB文件是一個二進制文件,經過此二進制文件可以還原數據庫狀態。
如圖:

Redis RDBRDB 數據還原

RDB提供兩種方式生成RDB文件,分別經過執行SAVE和BGSAVE命令生產RDB文件。SAVE由服務器進程直接執行保存,它會阻塞服務器的進程。BGSAVE是服務器子進程執行保存操做,它的運行不會阻塞服務器進程的運行。redis

SVAE 命令

上面已經說過,SAVE命令執行時會阻塞服務器的運行。所以,在服務器執行SAVE命令時,客戶端發送的全部命令請求都會被拒絕。只有服務器執行完SAVE命令,服務器纔會從新接收客戶端發送的命令請求。數據庫

BGSAVE 命令

與SAVE相反,BGSAVE由服務器子進程執行,在執行時依然能夠接收客戶端發送命令並處理。可是,服務器處理SAVE、BGSAVE、BGREWRITEAOF三種方式和平時有些不同。
數組

  1. 對於SAVE,在執行BGSAVE時,客戶端發送的SAVE命令會被服務器拒絕。服務器禁止SAVE和BGSAVE命令同時執行是爲了防止父進程(服務器進程)和子進行同時執行產生競爭條件
    安全

  2. 對於BGSAVE。在執行BGSAVE時,客戶端發送的BGSAVE命令時一樣也是被拒絕。緣由是爲了防止兩個BGSAVE產生競爭條件。
    服務器

  3. 對於BGREWRITEAOF。在執行BGSAVE時,也是被拒絕的。緣由是由於BGSAVE和BGREWRITEAOF不能同時執行。
    app

按期持久化原理

在陳述原理以前,先來看下幾個與按期持久化密切相關的屬性。
函數

  1. saveparams屬性

    在服務啓動後,服務器會讀取save的值賦給saveparams。save 是redis.conf中的一個配置文件,它在服務器中的默認配置爲:性能

save 900 1       //服務器在900秒以內,對數據庫進行了至少1次修改。
save 300 10           //服務器在300秒以內,對數據庫進行了至少10次修改。
save 60 10000         //服務器在60秒以內,對數據庫進行了至少10000次修改。

服務器狀態redisServer中saveparams結構以下:
spa

struct redisServer {    

           // ...    

           // 記錄了保存條件的數組    
           struct saveparam *saveparams;   

           // ...
      };

     struct saveparam {

          // 秒數
          time_t seconds;    

          // 修改數
         int changes;
      };

若是服務器中SAVE屬性爲默認配置,那麼服務器中的狀態將會下面這樣的。

RDB 狀態

  1. dirty計數器和lastsave屬性

    除了saveparams數組以外,服務器狀態還維持着一個dirty計數器,以及一個lastsave屬性:

  • dirty計數器記錄距離上一次成功執行SAVE命令或者BGSAVE命令以後,服務器對數據庫狀態(服務器中的全部數據庫)進行了多少次修改(包括寫入、刪除、更新等操做)。

  • lastsave屬性是一個UNIX時間戳,記錄了服務器上一次成功執行SAVE命令或者BGSAVE命令的時間。

    dirty和lastsave在服務器狀態中的結構以下:

struct redisServer {    

           // ...    

           // 修改計數器  
            long long dirty;  

           // 上一次執行保存的時間
             time_t lastsave;  

           // ...
      };



上面已經介紹完幾個重要的屬性了,如今開始切入正題了。

若是未開啓AOF功能,那麼在Redis啓動後,Redis的服務器週期性操做函數serverCron默認每隔100毫秒就會執行一次,它的其中一項工做就是檢查save選項所設置的保存條件是否已經知足。若是知足的話,就執行BGSAVE命令。下面是ServerCron函數的邏輯代碼

def serverCron():    
# …    
    # 遍歷全部保存條件    
    for saveparam in server.saveparams:       
         # 計算距離上次執行保存操做有多少秒       
         save_interval = unixtime_now()-server.lastsave    
    
        # 若是數據庫狀態的修改次數超過條件所設置的次數       
        # 而且距離上次保存的時間超過條件所設置的時間        
        # 那麼執行保存操做       
         if      server.dirty >= saveparam.changes and \
                   save_interval > saveparam.seconds:       
     
                BGSAVE(); 
     # ...

舉個例子,若是Redis服務器的當前狀態以下圖所示


那麼當時間來到1378271101,也便是1378270800的301秒以後,服務器將自動執行一次BGSAVE命令,由於saveparams數組的第二個保存條件——300秒以內有至少10次修改——已經被知足。

假設BGSAVE在執行5秒以後完成,那麼上圖所示的服務器狀態將更新爲下圖所示的狀態,其中dirty計數器已經被重置爲0,而lastsave屬性也被更新爲1378271106。

FINISHED

以上就是Redis服務器根據save選項所設置的保存條件,自動執行BGSAVE命令,進行間隔性數據保存的實現原理。

AOF 持久化

AOF持久化是經過保存Redis服務器所執行的寫命令來記錄數據庫狀態的。AOF持久化命令能夠分爲命令追加(append)、文件寫入、文件同步三個步驟。開啓AOF持久化的配置
appendonly yes

命令追加

resist服務器中的結構是這樣的

struct redisServer {
     // ...

     // AOF緩衝區
     sds aof_buf;

     // ...
}

服務器在執行一個寫命令後,會將命令追加到 aof_buf 緩衝區的末尾。

命令的寫入

將命令寫入到aof文件中去

將aof文件中的命令同步到磁盤

appendfsync配置說明

  1. appendfsync no (同步操做交給數據庫)
    當設置appendfsync爲no的時候,Redis不會主動調用fsync去將AOF日誌內容同步到磁盤,因此這一切就徹底依賴於操做系統的調試了。對大多數Linux操做系統,是每30秒進行一次fsync,將緩衝區中的數據寫到磁盤上。

  2. appendfsync everysec (每隔一秒執行一次同步操做)
    當設置appendfsync爲everysec的時候,Redis會默認每隔一秒進行一次fsync調用,將緩衝區中的數據寫到磁盤。可是當這一次的fsync調用時長超過1秒時。Redis會採起延遲fsync的策略,再等一秒鐘。也就是在兩秒後再進行fsync,這一次的fsync就無論會執行多 長時間都會進行。這時候因爲在fsync時文件描述符會被阻塞,因此當前的寫操做就會阻塞。

    結論就是,在絕大多數狀況下,Redis會每隔一秒進行一 次fsync。在最壞的狀況下,兩秒鐘會進行一次fsync操做。這一操做在大多數數據庫系統中被稱爲group commit,就是組合屢次寫操做的數據,一次性將日誌寫到磁盤。
  3. appendfsync always (每一次寫操做都會執行同步操做) 置appendfsync爲always時,每一次寫操做都會調用一次fsync,這時數據是最安全的,固然,因爲每次都會執行fsync,因此其性能也會受到響。

相關文章
相關標籤/搜索