本篇分析redis 如何處理set指令
客戶端解析使用者輸入、構造指令
redisformatsdscommandar**()
clisendcommand()
issuecommandrepeat()
repl()
例如使用者輸入set abcd 2
,則構造的指令就是*3\r\n$3\r\nset\r\n$4\r\nabcd\r\n$1\r\n2\r\n
,*
後面表示指令+引數的個數,$
後面表示當前引數的長度,然後緊跟引數值。
伺服器端,無論使用者輸入的是set
還是set xx px
還是其他set
引數,均有setcommand
函式執行,setcommand
函式簡單判斷否有nx
px
等然後提取超時時間,接著呼叫setgenericcommand
執行實際set操作。set指令儲存在字典裡面。
void
setcommand
(client *c)
elseif(
(a[0]==
'x'|| a[0]
=='x')&&
(a[1]==
'x'|| a[1]
=='x'
)&& a[2]
=='\0'&&!
(flags & obj_set_nx)
)elseif(
(a[0]==
'e'|| a[0]
=='e')&&
(a[1]==
'x'|| a[1]
=='x'
)&& a[2]
=='\0'&&!
(flags & obj_set_px)
&& next)
elseif(
(a[0]==
'p'|| a[0]
=='p')&&
(a[1]==
'x'|| a[1]
=='x'
)&& a[2]
=='\0'&&!
(flags & obj_set_ex)
&& next)
else
} c->ar**[2]
=tryobjectencoding
(c->ar**[2]
);setgenericcommand
(c,flags,c->ar**[1]
,c->ar**[2]
,expire,unit,
null
,null);
}
setgenericcommand
函式
void
setgenericcommand
(client *c,
int flags, robj *key, robj *val, robj *expire,
int unit, robj *ok_reply, robj *abort_reply)
if(unit == unit_seconds) milliseconds *
=1000;}
/*key存在,返回失敗,這符合set nx語義
* key不存在,返回失敗,這符合set xx語義
*/if
((flags & obj_set_nx &&
lookupkeywrite
(c->db,key)
!=null)||
(flags & obj_set_xx &&
lookupkeywrite
(c->db,key)
==null))
/*這是核心set指令的操作*/
setkey
(c->db,key,val)
; server.dirty++
;/*如果key設定了超時,例如setex 或者set px,則將key加入到expires的hash鍊錶
*超時這是乙個單獨的dict,下文會單獨說明為什麼需要expires
*/if(expire)
setexpire
(c,c->db,key,
mstime()
+milliseconds)
;notifykeyspaceevent
(notify_string,
"set"
,key,c->db->id);if
(expire)
notifykeyspaceevent
(notify_generic,
"expire"
,key,c->db->id)
;addreply
(c, ok_reply ? ok_reply : shared.ok)
;}
void
setkey
(redisdb *db, robj *key, robj *val)
else
/*這裡對val的引用計數++,因為外層會無條件對va引用計數--,這裡保證val不會被釋放*/
incrrefcount
(val)
;/*將key從expires移除,因為有這麼一種可能性,就是先setex key 設定的超時時間,後來沒超時後,又設定了 set key不帶超時時間,
*此時就要從expires中刪除。
*這裡無論此次操作是不是需要設定超時,都會無條件刪除,因為外層setgenericcommand會加expired。
*/removeexpire
(db,key)
;signalmodifiedkey
(db,key)
;}
setkey
的邏輯比較繞,按照正常理解,如果自己實現乙個set操作,首先應該就是查詢key,查到key則設定value,查不到就新建key,然後設定value。redis
考慮了函式的封裝性以及解耦,犧牲了部分效能。
其次,對應有超時需求的key,均會加入乙份key到db->expires
,他是單獨管理超時的hash鍊錶,為什麼需要單獨乙份?這是效能優化上的設計。假設你有100w個key,如果沒有db->expires
,每當判斷老化時,需要在包含100w個db->dict
裡面查詢所有,顯然這是不能接受的。有了db->expires
,查詢得到老化的key,key沒有則不查詢db->dict
,key有,則查詢db->expires
連指定的key。
我們看下dbadd
的情況,即key不存在,然後加key的情況。
void
dbadd
(redisdb *db, robj *key, robj *val)
set的操作,總的來說並不複雜,實際就是dictadd
,新增到字典,即hash表。 STL原始碼分析set
include include using namespace std int main set iset ia,ia 5 cout size iset.size endl cout 3 count iset.count 3 endl iset.insert 3 cout size iset.siz...
Redis原始碼分析之unlock
override public void unlock if opstatus 第一步 發布解鎖訊息刪除key protected rfutureunlockinnerasync long threadid 呼叫lua指令碼,exists命令 若 key 存在返回 1 否則返回 0 publish ...
redis之string原始碼分析
string資料型別的物件編碼有兩種,分別是embstr和raw。兩種編碼的區別並不大,embstr相對於raw,記憶體空間連續。兩者的資料格式見下圖 redis的string資料之所以使用embstr和raw兩種編碼格式,是為了當乙個string物件的值比較小時,使用乙個連續的記憶體分割槽存放re...