簡介
- RDB數(shù)據(jù)落地的2種方式塑娇。
- 3種觸發(fā)RDB數(shù)據(jù)落地的方式澈侠。
- RDB的主要流程。
- object落地的序列化埋酬。
- RDB數(shù)據(jù)落地的互斥哨啃。
2種落地方式方式
- 自身進(jìn)程阻塞式的進(jìn)行數(shù)據(jù)落地,這種方式會使得進(jìn)程阻塞写妥,無法處理其它命令拳球。
- fork子進(jìn)程進(jìn)行數(shù)據(jù)落地,父進(jìn)程繼續(xù)處理client的命令珍特,但這個(gè)時(shí)候父進(jìn)程會排斥其它落地的請求祝峻,任何RDB數(shù)據(jù)落地在子進(jìn)程完成之前都會拒絕,同時(shí)也會拒絕部分AOF數(shù)據(jù)落地請求扎筒。
3種觸發(fā)RDB數(shù)據(jù)落地的方式
關(guān)于數(shù)據(jù)恢復(fù):數(shù)據(jù)恢復(fù)的邏輯比較簡單呼猪,在redis啟動(dòng)時(shí),判斷指定目錄下是否有rdb文件砸琅,有則通過rdb文件進(jìn)行數(shù)據(jù)恢復(fù)。
- client通過命令觸發(fā)RDB轴踱,SAVE和BGSAVE命令症脂,分別對應(yīng)上面的2種落地方式。
- 通過配置文件配置落地條件,在serverCron中定期檢查條件是否滿足诱篷,滿足則進(jìn)行數(shù)據(jù)落地壶唤,該種落地方式默認(rèn)是fork子進(jìn)程異步的數(shù)據(jù)落地。配置的格式如下:
save time_ivl dirty_num // 格式
save 900 1 // 示例1
save 300 10 // 示例2
save 60 1000 // 示例3
配置的含義是棕所,如果距離上次RDB數(shù)據(jù)落地的時(shí)間超過time_ivl且修改的數(shù)據(jù)的數(shù)量大于dirty_num闸盔,那么就需要再次RDB數(shù)據(jù)落地。如果有多個(gè)配置琳省,則逐一比較迎吵,任何一個(gè)條件滿足,則進(jìn)行RDB數(shù)據(jù)落地针贬。
- 關(guān)機(jī)前進(jìn)行數(shù)據(jù)落地击费,這種方式通常都是自身進(jìn)程來執(zhí)行。
RDB的主要流程
-
RDB數(shù)據(jù)落地調(diào)用的函數(shù)為:rdbSaveRio桦他。redisdb的結(jié)構(gòu)如下圖蔫巩,每個(gè)redisserver可以有多個(gè)db,每個(gè)db會有一個(gè)dict快压,存儲鍵值對圆仔,其中鍵為sds字符串類型,值為object蔫劣,具體的類型由object中的type字段指定坪郭。(其實(shí)還有expire_dict,存儲的是具有過期屬性的鍵和對應(yīng)的到期時(shí)間)
RDB過程主要偽碼:
save(redis_version); // redis的版本號
save(save_info_aux_fields); // rdb的一些輔助字段
save(save_modules_aux); // rdb的輔助模塊
for db in server.db_arr:
save(RDB_OPCODE_SELECTDB); // 表示后續(xù)數(shù)據(jù)類型為db的下標(biāo)
save(db_index); // 存儲db的下標(biāo)
save(RDB_OPCODE_RESIZEDB); // 表示后續(xù)的數(shù)據(jù)是db的空間大小
save(db.db_size); // db的鍵的dict的size
save(db.expire_size); // db的設(shè)置了過期時(shí)間的鍵dict的size
for it in db.db_dict:
save(it.key); // sds db的dict的鍵
save(it.value); // object db的dict的值
for it in db.expire_dict:
save(it.key); // sds expire_dict中的鍵
save(it.value); // int expire_dict中到期時(shí)間
上面的dict中的key-value在內(nèi)存中通常是非連續(xù)的內(nèi)存拦宣,要進(jìn)行序列化才能寫入到文件中截粗。
object的序列化
1. 主要的object
在了解序列化之前,需要先了解redis中主要有哪些object鸵隧。
2. object的屬性绸罗。
redis中,object有2個(gè)類型字段豆瘫,一個(gè)是type珊蟀,一個(gè)是encoding,其中type表示上層業(yè)務(wù)使用對應(yīng)的類型外驱,encoding表示底層的實(shí)現(xiàn)育灸。以Hash為例,在業(yè)務(wù)層表示的是該結(jié)構(gòu)是鍵值對類型的數(shù)據(jù)結(jié)構(gòu)昵宇,必須實(shí)現(xiàn)鍵值對所應(yīng)該具有的接口磅崭,hashtable和ziplist是其2種不同的底層實(shí)現(xiàn)。
3. 連續(xù)內(nèi)存存儲的object
用連續(xù)內(nèi)存存儲的object類型(特點(diǎn)是數(shù)據(jù)都存儲在連續(xù)的內(nèi)存中瓦哎,在落地時(shí)砸喻,直接將對應(yīng)內(nèi)存寫到文件中即可)有:int柔逼,sds,ziplist割岛,intset愉适,zipmap(已不再使用)。這些object最終都可以通過以下方式寫入dump文件癣漆。
if ((n = rdbSaveLen(rdb,len)) == -1) return -1; // 先存值的長度
nwritten += n;
if (len > 0) {
if (rdbWriteRaw(rdb,s,len) == -1) return -1; // 再將值寫入到文件中
nwritten += len;
}
static int rdbWriteRaw(rio *rdb, void *p, size_t len) {
if (rdb && rioWrite(rdb,p,len) == 0)
return -1;
return len;
}
4. 非連續(xù)內(nèi)存存儲的數(shù)據(jù)的序列化
在這之前需要了解復(fù)合的數(shù)據(jù)類型维咸,redis中的最基本的數(shù)據(jù)類型是sds和數(shù)值(int, double等)這2種類型。其它的類型都是由這2種類型組合而成惠爽。上面提到的每個(gè)db都有個(gè)dict癌蓖,存儲了該db中所有的數(shù)據(jù),其中key為sds類型疆股,value為object费坊,object可能的類型和對應(yīng)的客戶端命令關(guān)系如下。
命令 | object的type | object的encoding | key的類型 | value的類型 |
---|---|---|---|---|
db.dict(特指db中的dict) | hash | dict | sds | object |
set | string | sds | 無key | 無value |
sadd | set | hashtable/ziplist | sds | 無value |
hset | hash | hashtable/ziplist | sds | sds |
lpush | list | linklist/ziplist/quicklist | sds | 無value |
zset | zset | skiplist/quicklist | sds | double |
從上面的復(fù)合數(shù)據(jù)類型的分析可知旬痹,db中的object的復(fù)合類型最復(fù)雜的hash也只是由sds類型的K-V鍵值對組成附井。hashtable的序列化代碼如下:
} else if (o->type == OBJ_HASH) {
/* Save a hash value */
if (o->encoding == OBJ_ENCODING_ZIPLIST) {
if ((n = rdbSaveRawString(rdb,o->ptr, ziplist_len)) == -1) return -1;
} else if (o->encoding == OBJ_ENCODING_HT) {
rdbSaveLen(rdb,dictSize((dict*)o->ptr))) // 先存儲鍵值對的數(shù)量
while((de = dictNext(di)) != NULL) {
rdbSaveRawString(rdb,(unsigned char*)field, sdslen(field))); // 將key以sds類型進(jìn)行存儲
rdbSaveRawString(rdb,(unsigned char*)value, sdslen(value))); // 將value以sds類型進(jìn)行存儲
}
}
復(fù)合的數(shù)據(jù)類型都是基于sds和數(shù)值這2種基本類型組合而成,最終都可以變成基本類型的存儲两残。
數(shù)據(jù)落地之間的沖突關(guān)系
1.RDB數(shù)據(jù)落地期間永毅,其它的RDB請求都會被拒絕執(zhí)行,至于AOF數(shù)據(jù)落地人弓,待確定沼死。
總結(jié)
RDB數(shù)據(jù)落地主要掌握以下幾點(diǎn):
- 2種數(shù)據(jù)落地方式。
- 3種觸發(fā)RDB的方式崔赌。
- RDB落地的主要流程意蛀,了解了該流程即可知道RDB文件內(nèi)容的格式。
- RDB數(shù)據(jù)落地需要把每個(gè)db中的data_dict和expire_dict進(jìn)行序列化健芭,其中data_dict的value為object類型县钥,需要知道object類型如何進(jìn)行序列化。