Redis SNAPSHOT的實現

來源:互聯網
上載者:User

Redis SNAPSHOT

上一篇文章我們學習了redis aof的實現.這篇文章我們將學習redis的另一種持久化方式:snapshot(快照)。
同上一篇文章一樣,我們首先介紹相關參數;然後依次介紹它的使用情境。

1. 配置參數
save <seconds> <changes>:相對一個DB,多少秒內發生了多少次更新操作,此時就會進行一次儲存操作,這個可以設定多個條件,它們中的任一個滿足都會儲存一次,下面把這個配置叫做時間變化條件。
rdbcompression yes:是否進行壓縮操作
dbfilename dump.rdb:資料檔案的名字
dir /u01/xiangzhong/redis-2.4.2/data:上面檔案儲存的位置

2.使用情境
通過上面的參數save <seconds> <changes>我們可以知道當觸發這個條件的時候就會執行一次save rdb操作。所以我們先來看一下這個過程。
2.1 save <seconds> <changes>條件觸發自動運行
該過程在serverCron裡判斷

        /* If there is not a background saving in progress check if         * we have to save now */         for (j = 0; j < server.saveparamslen; j++) {//判斷總共有多少個時間變化條件,這個總共由三部分組成,系統啟動時initServerConfig初始化三個[(60*60,1),(300,100),(60,10000)];然後就是載入設定檔loadServerConfig;最後就是client通過命令來添加條件;它們被儲存到server.saveparams這個動態數組裡,在追加的時候並沒有對覆蓋情況進行去重,不過這個資料不會太大,所以這個影響不大            struct saveparam *sp = server.saveparams+j;            if (server.dirty >= sp->changes &&                now-server.lastsave > sp->seconds) { //這裡就是簡單的每個條件匹配                redisLog(REDIS_NOTICE,"%d changes in %d seconds. Saving...",                    sp->changes, sp->seconds);                rdbSaveBackground(server.dbfilename); //產生後檯子進程進行寫操作                break;            }         }

這裡我們不再介紹rdbSaveBackground函數,而直接介紹子進程的處理rdbSave:

int rdbSave(char *filename) {...    snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid()); //產生一個新的臨時檔案    fp = fopen(tmpfile,"w");    if (fwrite("REDIS0002",9,1,fp) == 0) goto werr;    for (j = 0; j < server.dbnum; j++) {        redisDb *db = server.db+j;        dict *d = db->dict;        if (dictSize(d) == 0) continue;        di = dictGetSafeIterator(d);        //寫select db 命令,只是這裡使用的是編碼的方式,而不是可讀的字元命令        if (rdbSaveType(fp,REDIS_SELECTDB) == -1) goto werr;        if (rdbSaveLen(fp,j) == -1) goto werr;//db id        while((de = dictNext(di)) != NULL) { //遍曆該db內的所有表的所有記錄entry            sds keystr = dictGetEntryKey(de); //獲得key值            robj key, *o = dictGetEntryVal(de); //獲得value            time_t expiretime;                        initStaticStringObject(key,keystr); //key有兩種可能encode:RAW,INT這裡直接使用RAW,只有是資料的才可能被encode為INT,所以RAW是安全,可靠的            expiretime = getExpire(db,&key);            if (expiretime != -1) {                //判斷該key是否已經到期,如果到期了就不儲存了直接跳過                if (expiretime < now) continue;                if (rdbSaveType(fp,REDIS_EXPIRETIME) == -1) goto werr;                if (rdbSaveTime(fp,expiretime) == -1) goto werr;            }            if (!server.vm_enabled || o->storage == REDIS_VM_MEMORY ||                                      o->storage == REDIS_VM_SWAPPING) {                int otype = getObjectSaveType(o); //獲得value的類型                /* Save type, key, value */                if (rdbSaveType(fp,otype) == -1) goto werr;                if (rdbSaveStringObject(fp,&key) == -1) goto werr;                if (rdbSaveObject(fp,o) == -1) goto werr;            }     ...        } //end while dict entry  dictReleaseIterator(di);    } //end for db    if (rdbSaveType(fp,REDIS_EOF) == -1) goto werr;    fflush(fp);    fsync(fileno(fp));    fclose(fp);    rename(tmpfile,filename);    server.dirty = 0;  //對於子進程這兩個值更新沒有作用,但如果是使用save命令,則它是在主線程裡進行所以必須更新    server.lastsave = time(NULL);    ...}

該函數相對於aof的rewriteAppendOnlyFile簡單得多,裡面的注釋也很清楚,關於value的type與encode可參考:http://www.w3ccollege.org/redis/redis-internal/redis-memory-storage-structure-analysis-2.html
當後檯子進程結束的時候,主線程在serverCron裡wait該訊號(這與aof的時機一樣),我們這裡直接看一下訊號處理函數backgroundSaveDoneHandler

    server.dirty = server.dirty - server.dirty_before_bgsave; //更新dirty    server.lastsave = time(NULL);    server.bgsavechildpid = -1;    updateSlavesWaitingBgsave(exitcode == 0 ? REDIS_OK : REDIS_ERR); //這個與repl slave有關,暫不介紹

2.2用戶端命令觸發
上面我們介紹了系統自動檢測時間改變條件,完成save rdb的過程,下面我們將介紹另外兩種情況它們都是由用戶端通過命令來觸發的:save、bgsave。
A.save
該命令的回呼函數為:saveCommand

void saveCommand(redisClient *c) { //如果現在後面剛好有一個進程在save,顯然不再需要了,另外該函數也是通過調用rdbSave來實現的。    if (server.bgsavechildpid != -1) {        addReplyError(c,"Background save already in progress");        return;    }    if (rdbSave(server.dbfilename) == REDIS_OK) {        addReply(c,shared.ok);    } else {        addReply(c,shared.err);    }}

註:該方式是在主線程中執行save rdb操作,所以會阻塞主線程的工作。所以一般不建議使用該命令,而使用下面的命令。

B.bgsave
該命令的回呼函數為:bgsaveCommand,該函數也是通過調用rdbSaveBackground來實現,即其實這個過程跟上面的server判斷時間改變條件的過程是一樣,只是後者是由用戶端通過命令來強制它執行,而不是像前者那樣等到條件滿足時才執行。所以我們這裡也不再贅述。

3.總結
redis的SNAPSHOT有三種應用情境:其一在每次運行serverCron的時候去檢測時間改變條件是否滿足,如果滿足就會建立一個後檯子進程進行記憶體資料到dump.rdb檔案的寫過程:將記憶體資料寫到一個tempfile,然後再rename(也就是每次都是把整個記憶體資料儲存起來,而不是修改更新的資料);用戶端發送save命令,此時是在主線程中執行的,所以會阻塞主線對file event的響應;最後一種跟第一種很相似只是它是通過用戶端發送bgsave來實現。另外之所以稱為SNAPSHOT,是因為它是利用fork子進程與父進程是通過copy-on-write的方式來暫時共用記憶體位址空間的,而不影響父進程。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.