Redis的AOF、RDB持久化方式

Redis作爲一款內存數據庫,它將數據存儲在內存裏面,是存在Redis進程由於某種原因推出導致數據丟失的風險的,因此瞭解Redis數據的持久化是很有必要,Redis提供兩種持久化方式,分別是AOF和RDB,下面將着重介紹這兩種持久化方式

RDB持久化

這種方式是指在指定的時間內將內存中的數據集快照寫進磁盤,RDB持久化產生的RDB文件是一個經過壓縮的二進制文件,這個文件被保存在磁盤中,Redis可通過該文件恢復數據庫當時的狀態。Redis提供兩個命令給我們進行觸發RDB持久化,一個是save,當我們執行save命令,Redis進程將會阻塞進行RDB持久化過程,期間不執行其他的命令請求。另一個是bgsave,這個命令可以在不阻塞服務器進程的情況下執行(通過fork一個子進程執行)。
我們可以在redis.conf配置文件中配置RDB自動間歇性保存數據的策略,譬如:
在這裏插入圖片描述
上圖紅框裏面的三個條件只要滿足其中任意一個,都會觸發服務器執行bgsave命令:

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

RDB保存數據

int rdbSaveBackground(char *filename) {
    pid_t childpid;
    long long start;

    // 如果 BGSAVE 已經在執行,那麼出錯
    if (server.rdb_child_pid != -1) return REDIS_ERR;

    // 記錄 BGSAVE 執行前的數據庫被修改次數
    server.dirty_before_bgsave = server.dirty;

    // 最近一次嘗試執行 BGSAVE 的時間
    server.lastbgsave_try = time(NULL);

    // fork() 開始前的時間,記錄 fork() 返回耗時用
    start = ustime();

    if ((childpid = fork()) == 0) {
        int retval;

        /* Child */

        // 關閉網絡連接 fd
        closeListeningSockets(0);

        // 設置進程的標題,方便識別
        redisSetProcTitle("redis-rdb-bgsave");

        // 執行保存操作
        retval = rdbSave(filename);

        // 打印 copy-on-write 時使用的內存數
        if (retval == REDIS_OK) {
            size_t private_dirty = zmalloc_get_private_dirty();

            if (private_dirty) {
                redisLog(REDIS_NOTICE,
                    "RDB: %zu MB of memory used by copy-on-write",
                    private_dirty/(1024*1024));
            }
        }

        // 向父進程發送信號
        exitFromChild((retval == REDIS_OK) ? 0 : 1);

    } else {

        /* Parent */

        // 計算 fork() 執行的時間
        server.stat_fork_time = ustime()-start;

        // 如果 fork() 出錯,那麼報告錯誤
        if (childpid == -1) {
            server.lastbgsave_status = REDIS_ERR;
            redisLog(REDIS_WARNING,"Can't save in background: fork: %s",
                strerror(errno));
            return REDIS_ERR;
        }

        // 打印 BGSAVE 開始的日誌
        redisLog(REDIS_NOTICE,"Background saving started by pid %d",childpid);

        // 記錄數據庫開始 BGSAVE 的時間
        server.rdb_save_time_start = time(NULL);

        // 記錄負責執行 BGSAVE 的子進程 ID
        server.rdb_child_pid = childpid;

        // 關閉自動 rehash
        updateDictResizePolicy();

        return REDIS_OK;
    }

    return REDIS_OK; /* unreached */
}

上面是Redis源碼 RDB bgsave保存數據的一個方法,可以看到,if ((childpid = fork()) == 0)這行代碼就是fork一個子進程,調用fork()函數會創建一個和父線程幾乎一模一樣的子線程(只有少數值與原來的進程的值不同),fork()函數實際會返回兩個值,如果線程是子線程,返回值是0,如果是父線程返回值是子線程的進程ID。從源碼上看Redis RDB bgsave方式就是依靠fork出來的子線程來進行保存數據的操作,保存數據完畢後會通知父進程。

RDB持久化恢復數據過程

下面是3.0版本RedisRDB持久化恢復數據的流程圖
在這裏插入圖片描述

優劣勢

優勢:

  • 通過fork子進程後臺執行的方式進行持久化,父進程不需要再做其他IO操作,能較大優化Redis性能
  • RDB文件都是比較緊湊的,恢復也比較快

劣勢:

  • 由上面的敘述知道RDB持久化方式一般是自動間歇性的,存在較大的丟失數據風險
  • RDB持久化由於是fork子進程的方式,如果要保存的數據集很大,fork的過程耗時較長(保存在內存的數據集要複製一份),可能會導致整個服務器停止服務幾百毫秒,設置1秒。

AOF持久化

AOF,Append Only File,它的意思是以日誌的形式來記錄每個寫操作,將Redis執行過的所有寫指令記錄下來(讀操作不記錄),只許追加文件但不可以改寫文件,Redis啓動之初會讀取該文件重新構建數據,換言之,Redis重啓的話就根據日誌文件的內容將寫指令從前到後執行一次以完成數據的恢復工作。可以說AOF和RDB的最大區別就是,AOF每次執行是追加變更數據庫狀態的操作日誌到文件中,而RDB是把目前的數據庫所有數據全部dump到文件中,是一種快照的方式。
AOF通過appendfsync指定,默認爲everysec:

  • always: 將緩存區的內容總是即時寫到AOF文件
  • everysec: 每隔一秒就將緩存區的內容寫到AOF文件
  • no: 寫入AOF文件中的操作由操作系統決定,一般而已,爲了提高效率會等待緩存區被填滿,纔會開始同步數據到磁盤

AOF重寫

上面提到,AOF持久化方式會根據appendfsync配置,不斷的執行追加更新數據庫狀態操作日誌到文件中,這樣隨着時間推移,AOF文件會越來越大,有大量的冗餘操作日誌,比如,set a 100,set a 200,set a 100,這三條命令在AOF文件中恢復數據要執行三次,實際上執行一次set a 100即可。這個時候,Redis利用AOF重寫功能,來控制AOF文件大小。AOF重寫功能會首先讀取數據庫中現有的鍵值對狀態,然後根據類型使用一條命令來替代之前的鍵值對多條命令。
由於AOF重寫功能有大量的寫操作,Redis在重寫時候也是通過fork子進程的方式來執行的,父進程仍然處理新的更新操作請求。這樣是可能存在執行重寫操作期間,數據不一致的情況,因此Redis還設置了一個AOF重寫緩衝區,這個緩衝區在子線程被創建開始之後使用,這個期間,所有的命令會存兩份,一份在AOF緩存空間,一份在AOF重寫緩衝區,當AOF重寫完成之後,子進程會發信號給父進程,通知父線程將AOF重寫緩衝區的內容添加到AOF文件中。

優劣勢

優勢:

  • 按照默認配置,最多丟失1秒數據
  • 如果日誌過大,可以重寫控制日誌文件大小
  • 寫入方式是append,沒有磁盤尋址開銷,性能高

劣勢:

  • 相同數據集的數據而言AOF文件要遠大於RDB文件,恢復速度慢於RDB
  • AOF運行效率要慢於RDB,每秒同步策略效率較好,不同步效率和RDB相同

小結

總的來說,AOF,RDB這兩種方式各有優劣勢,使用哪種方式,要視場景而言,如果要追求很高的數據完備性,推薦用AOF的方式,默認配置最壞情況下只會丟失1秒的數據,如果對丟失數據不是特別敏感,用RDB其實也夠了。當然如果你只希望你的數據在服務器運行的時候存在,你也可以不使用任何持久化方式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章