1. 程式人生 > >Redis 設計與實現(第九章) -- 持久化RBD

Redis 設計與實現(第九章) -- 持久化RBD

key fork amount del pty str server int name

概述


Redis為內存數據庫,即所有的鍵值對信息保存在內存中,那麽一旦服務器出現問題重啟,內存中的數據就會沒有了。所以Redis需要實現持久化,將內存中的數據持久化到硬盤,在重新啟動後,又將硬盤中的數據加載到內存中。

RDB文件生成與載入

  • 有兩個命令可用於生成RDB文件,save和bgsave:

Save:save命令會阻斷服務器進程,直到RDB文件生成,在阻塞期間,Redis服務器不會出現任何請求。

bgsave:bgsave命令會派生出一個子進程,然後由子進程來創建RDB文件,服務器進程繼續出現請求命令。

  • Redis中RDB文件載入沒有專門的命令,只要在redis服務器重啟的過程中,檢測到rdb文件的存在就會自動加載。(這裏先忽略AOF的情況,因為在AOF開啟的情況下,服務器會優先加載AOF文件,因為AOF的更新頻率通常比較RDB的高)
  • 命令執行的服務器狀態
    • SAVE命令會阻塞服務器的請求,也就是保存期間,如果服務器有新的請求過來,不會去處理請求直到RDB文件生成;
    • BGSAVE命令,會fork一個子進程出來,所以服務器能夠處理其他請求命令;
    • 如果在執行BGSAVE命令時,客戶端再次執行SAVE,BGSAVE或BGREWRITEAOF呢?
      • 不能同時執行,避免競爭條件發生。
    • 在RDB文件載入時,所有請求均處於阻塞狀態。

我們不可能每次手動去調用命令來持久化,Redis自身肯定是有一套策略的,什麽時候進行持久化。

默認情況下,redis在下列條件滿足其一,BGSAVE命令就會執行(可通過配置修改):

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

可以看一下數據結構:
在redisServer的數據結構中,有個屬性用於記錄保存條件的修改:

/* RDB persistence */
    long long dirty;                /* Changes to DB from the last save */
    long long dirty_before_bgsave;  /* Used to restore dirty on failed BGSAVE */
    pid_t rdb_child_pid;            
/* PID of RDB saving child */ struct saveparam *saveparams; /* Save points array for RDB */

再看下saveparam的數據結構:

struct saveparam {
    time_t seconds;  //時間
    int changes;  //修改數
};

此外,redisServer中還有兩個屬性dirty和lastsave:

/* RDB persistence */
    long long dirty;                /* Changes to DB from the last save,上次保存命令執行後,數據庫的修改次數 */
    long long dirty_before_bgsave;  /* Used to restore dirty on failed BGSAVE */
    pid_t rdb_child_pid;            /* PID of RDB saving child */
    struct saveparam *saveparams;   /* Save points array for RDB */
    int saveparamslen;              /* Number of saving points */
    char *rdb_filename;             /* Name of RDB file */
    int rdb_compression;            /* Use compression in RDB? */
    int rdb_checksum;               /* Use RDB checksum? */
    time_t lastsave;                /* Unix time of last successful save ,上次成功保存的時間*/

RedisServer會定期執行serverCron(100ms一次),每次執行時,會檢測之前默認的條件是否滿足,如果滿足條件則執行bgsave命令。程序會遍歷saveparam中的數組,判斷是否滿足條件。

/* If there is not a background saving/rewrite in progress check if(之前會判斷是否有AOF或BSAVE命令正在執行)
         * we have to save/rewrite now */
         for (j = 0; j < server.saveparamslen; j++) {
            struct saveparam *sp = server.saveparams+j;

            /* Save if we reached the given amount of changes,
             * the given amount of seconds, and if the latest bgsave was
             * successful or if, in case of an error, at least
             * REDIS_BGSAVE_RETRY_DELAY seconds already elapsed. */
            if (server.dirty >= sp->changes &&
                server.unixtime-server.lastsave > sp->seconds &&
                (server.unixtime-server.lastbgsave_try >
                 REDIS_BGSAVE_RETRY_DELAY ||
                 server.lastbgsave_status == REDIS_OK))
            {
                redisLog(REDIS_NOTICE,"%d changes in %d seconds. Saving...",
                    sp->changes, (int)sp->seconds);
                rdbSaveBackground(server.rdb_filename);
                break;
            }
         }

查看odb文件,如下,set 一個msgkey的內容為aaa,rdb文件如下:

redis 127.0.0.1:6379> keys *
(empty list or set)
redis 127.0.0.1:6379> set msg aaa

redis 127.0.0.1:6379> BGSAVE

Background saving started
[[email protected] bin]# od -c dump.rdb
0000000 R E D I S 0 0 0 6 376 \0 \0 003 m s g
0000020 003 a a a 377 241 362 266 \f Z ` 030 265
0000035

0000000 R E D I S 0 0 0 6 :代表RDB文件標誌及版本

376 \0 :切換到數據庫0

\0 :類型,代表字符串類型

003 m s g:003代表key長度,msg為鍵

003 a a a:003代表內容長度,aaa代表值

377:代表EOF

後面的241.。。。。265:代表校驗和

可以再繼續看一個切換到db 1的list集合

redis 127.0.0.1:6379> select 1
OK
redis 127.0.0.1:6379[1]> rpush listkey "aa" "ccc"
(integer) 2
redis 127.0.0.1:6379[1]> BGSAVE
Background saving started
redis 127.0.0.1:6379[1]>
You have mail in /var/spool/mail/root
[[email protected] bin]# od -c dump.rdb
0000000 R E D I S 0 0 0 6 376 001 \n \a l i s
0000020 t k e y 024 024 \0 \0 \0 016 \0 \0 \0 002 \0 \0
0000040 002 a a 004 003 c c c 377 377 } y 232 302 X h
0000060 g &
0000062

Redis 設計與實現(第九章) -- 持久化RBD