Redis源碼分析系列二十四: 7 set---setCommand解析

這個命令應該是打通不少命令的關鍵點,估計也是用的最多的命令了。 數據結構

因此必須一字一句的來剖析這個命令的本質! 函數

我只能說:全部的反動派都是紙老虎! 源碼分析

注意:這個操做的數據影響server.db[index].dict哈希表。 學習

看完這個函數以後:我只想說:若是設置了expire,也會影響server.db[index].expires哈希表 ui

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 編碼


 int j;
//設置一個整型變量


robj *expire = NULL;
//設置一個robj類型的指針變量


int unit = UNIT_SECONDS;
//設置unit默認爲1 spa


int flags = REDIS_SET_NO_FLAGS;
//默認設置flags爲0
//自定義檢查點: 1 2 3 指針

~~~~~~~~~~~~~~~~~~~~~~~~~~~ code

for (j = 3; j < c->argc; j++) 
{


//從set key value後面的參數中開始提取內容

char *a = c->argv[j]->ptr;
//指向當前參數的值

robj *next = (j == c->argc-1) ? NULL : c->argv[j+1];
//獲取下一個參數,可能爲NULL server

~~~~~~~~~~~~~~~~~~~~~~~~~~

if ((a[0] == 'n' || a[0] == 'N') &&
(a[1] == 'x' || a[1] == 'X') && a[2] == '\0') 
{
flags |= REDIS_SET_NX;


//若是是n|N,  x|X,則設置flags標識加上1

~~~~~~~~~~~~~~~~~~~~~~~~~~

else if ((a[0] == 'x' || a[0] == 'X') &&
  (a[1] == 'x' || a[1] == 'X') && a[2] == '\0') 
{
flags |= REDIS_SET_XX;


//若是是x|X, x|X,則設置flags標識加上2

~~~~~~~~~~~~~~~~~~~~~~~~~~

else if ((a[0] == 'e' || a[0] == 'E') &&
  (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' && next) 
{
unit = UNIT_SECONDS;

expire = next;


j++;
}


//若是是e|E, x|X,則設置unit= 0,expire指向下一個節點

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

else if ((a[0] == 'p' || a[0] == 'P') &&
  (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' && next) 
{

unit = UNIT_MILLISECONDS;

expire = next;

j++;


//若是是p|P, x|X,則設置unit = 1,expire指向下一個節點

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

而後我以爲一個很關鍵的地方是: tryObjectEncoding函數,下面開始分析這個函數。

PS:看這個函數以前,我特地翻閱了processInlineBuffer代碼,裏面有這麼一行:

c->argv[c->argc] = createObject(REDIS_STRING,argv[j]);

也就是說,目前的參數的形式都是: REDIS_STRING.

好,下面能夠正式分析tryObjectEncoding

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

long value;
//設置一個long類型變量

sds s = o->ptr;
//指向一個字符串

size_t len;
//設置一個size_t類型的變量


if (o->encoding != REDIS_ENCODING_RAW)
return o; /* Already encoded */
//若是encoding方式是REDIS_ENCODING_RAW,則不用編碼
//顯然上面會執行,由於剛進來都是REDIS_STRING

那隻好直接返回了

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

如今回到setCommand函數。

最後一行代碼:setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL);

那就進入setGenericCommand函數來看看本質吧。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

long long milliseconds = 0; /* initialized to avoid any harmness warning */
//初始化爲 0
//自定義檢查點: 1 2 3

if (expire) 
{


//若是設置了時間

if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK)
return;
//提取參數放到milliseconds裏去

if (milliseconds <= 0) 
{
addReplyError(c,"invalid expire time in SETEX");
return;
}
//對有效性進行驗證
//自定義檢查點: 1  2 3


if (unit == UNIT_SECONDS) 
milliseconds *= 1000;
//若是是秒,還要換算成毫秒

}

~~~~~~~~~~~

if (
(flags & REDIS_SET_NX && lookupKeyWrite(c->db,key) != NULL)
||
(flags & REDIS_SET_XX && lookupKeyWrite(c->db,key) == NULL)
)
{
addReply(c, abort_reply ? abort_reply : shared.nullbulk);
return;
}
判斷有效性

~~~~~~~~~~

下面是最關鍵的函數setKey(c->db,key,val);

開始看這個函數。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

這個函數調用了lookupKeyWrite(db,key)。

而後lookuKeyWrite又調用了lookupKey。

因此我只好去看lookupKey函數。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

dictEntry *de = dictFind(db->dict,key->ptr);//去哈希表裏找出這個節點

if (de) 
{
//若是節點存在
robj *val = dictGetVal(de);
//取出value

if (server.rdb_child_pid == -1 && server.aof_child_pid == -1)
val->lru = server.lruclock;
//更新訪問時間

return val;
} else {
return NULL;
}
//返回當前值或者NULL
lookupKey函數結束,返回到函數lookupKeyWrite。

而後lookupKeyWrite函數也結束了,

回到setKey函數中來繼續執行。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

if (lookupKeyWrite(db,key) == NULL) 
{
//若是找不到,則增長
增長的代碼是: dbAdd(db,key,val);

~~~~~~~~~研究dbAdd函數。

dbAdd調用dictAdd,dictAdd調用dictAddRaw,

這樣就把一個(key,value)加入到了哈希表中去。

獲得這個結果然爽!

~~~~~~~~~~~~~~~~~~~~~~

若是已經存在,則調用dbOverwrite函數。

這個是經過dictReplace函數實現。

具體的就不分析了

~~~~~~~~~~~~~~~~~~~~~~

incrRefCount實現增長引用次數。

~~~~~~~

其它的暫時不深刻。

我以爲不少東西不是一次性弄懂的,須要循環的按部就班方式來整理才能夠。

學習不是一蹴而就的東西!

~~~~~~~~~~~

既然setKey函數結束了,咱們回到setGenericCommand函數中。

server.dirty++;
//表示有多少個髒數據

if (expire) 
{
//若是設置了超時時間,則設置超時時間到節點裏去
//單位:毫秒
setExpire(c->db,key,mstime()+milliseconds);
}
setExpire函數頗有意思,

是先從dict哈希表裏找出節點,

而後添加/查找到一樣key的節點,設置s64變量爲超時的毫秒。

union {
        void *val;
        uint64_t u64;
        int64_t s64;
    } v;

具體參見上面的數據結構。

~~~~~~~~~~~~

個人疑問:爲何要放一份只帶key的數據到expires哈希表中去呢?

直接在dict表中設置也能夠。或許是考慮到競爭使用的問題?

~~~~~~~~~~~~~~~~~~~

setExpire函數就此結束。

回到setGenericCommand函數繼續執行。

notifyKeyspaceEvent暫且不考慮。

最後天然是發送"+OK\r\n"返回給客戶了。

setGenericCommand就此結束,返回到setCommand,

setCommand就此結束。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

總結:

client:  SET key value [NX] [XX] [EX <seconds>] [PX <milliseconds>] */

server: "+OK\r\n"

對Redis的源碼分析系列暫時到此爲止!後續會不按期更新,敬請關注!

文章若轉載,必須添加本文原始連接。

版權全部,侵權必究!

                            ————強子哥哥  837500869

相關文章
相關標籤/搜索