日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

Redis源码剖析(七)监视功能

發(fā)布時(shí)間:2024/4/19 数据库 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redis源码剖析(七)监视功能 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Redis提供這樣一個(gè)功能,客戶端在開啟事務(wù)之前,可以設(shè)置對(duì)一個(gè)或多個(gè)鍵的監(jiān)視,在執(zhí)行EXEC命令之前的這段時(shí)間,如果其他客戶端對(duì)該客戶端監(jiān)視的鍵做了修改,那么Redis會(huì)取消該客戶端事務(wù)的運(yùn)行,也就是說(shuō)如果執(zhí)行EXEC,那么Redis什么也不會(huì)做。

由于別的客戶端對(duì)當(dāng)前客戶端關(guān)心的鍵做了修改,Redis會(huì)將當(dāng)前客戶端的事務(wù)視為不安全,從而不再執(zhí)行。Redis設(shè)計(jì)與實(shí)現(xiàn)一書中將監(jiān)視功能稱作樂(lè)觀鎖

監(jiān)視命令

監(jiān)視功能由WATCH命令實(shí)現(xiàn),可以設(shè)置對(duì)一個(gè)或多個(gè)鍵的監(jiān)視

127.0.0.1:6379> set time 14:33 //設(shè)置鍵值對(duì) OK 127.0.0.1:6379> WATCH time //設(shè)置對(duì)鍵time的監(jiān)視 OK 127.0.0.1:6379> MULTI //開啟事務(wù) OK 127.0.0.1:6379> set db redis QUEUED 127.0.0.1:6379> get db QUEUED 127.0.0.1:6379> get time QUEUED 127.0.0.1:6379> //此時(shí)還處于事務(wù)狀態(tài),沒(méi)有執(zhí)行EXEC命令

此時(shí)開啟另一個(gè)客戶端對(duì)鍵time進(jìn)行修改

注:修改的意思通常是改變鍵對(duì)應(yīng)的值,使用各種SET命令

127.0.0.1:6379> set time 14:34 //修改鍵time的值 OK 127.0.0.1:6379>

現(xiàn)在回到之前的客戶端,如果執(zhí)行EXEC命令,會(huì)發(fā)現(xiàn)事務(wù)隊(duì)列中的命令沒(méi)有執(zhí)行,而是返回了nil

//執(zhí)行事務(wù)之前 127.0.0.1:6379> set time 14:33 OK 127.0.0.1:6379> WATCH time OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set db redis QUEUED 127.0.0.1:6379> get db QUEUED 127.0.0.1:6379> get time QUEUED //執(zhí)行事務(wù)之后 127.0.0.1:6379> EXEC //執(zhí)行事務(wù),返回空 (nil) 127.0.0.1:6379>

存儲(chǔ)結(jié)構(gòu)

要想知道一個(gè)客戶端都監(jiān)視了哪些鍵,就需要將其記錄下來(lái),Redis采用字典記錄每個(gè)被監(jiān)視的鍵和監(jiān)視該鍵的所有客戶端,不過(guò)這個(gè)字典不在redisServer結(jié)構(gòu)中,而是在數(shù)據(jù)庫(kù)redisDb結(jié)構(gòu)中,該結(jié)構(gòu)中保存了很多字典,其中就有數(shù)據(jù)鍵值對(duì)字典和過(guò)期時(shí)間字典,之前提到過(guò)的

//server.h typedef struct redisDb {dict *dict; /* 保存鍵值對(duì)的字典 */ dict *expires; /* 保存鍵和其到期時(shí)間 */ dict *watched_keys; /* 監(jiān)視字典,保存每個(gè)被監(jiān)視的鍵和所有監(jiān)視該鍵的客戶端 */ ... } redisDb;

監(jiān)視字典中保存了所有客戶端的監(jiān)視信息,鍵是每個(gè)被監(jiān)視的鍵,值是監(jiān)視該鍵的客戶端鏈表

和訂閱模塊相同,客戶端同時(shí)也會(huì)記錄自己監(jiān)視了哪些鍵,不過(guò)客戶端不需要使用字典,使用鏈表就夠了。在client結(jié)構(gòu)中,可以找到相關(guān)的定義

//server.h typedef struct client {list *watched_keys; //監(jiān)視鏈表,記錄當(dāng)前客戶端監(jiān)視的所有鍵... } client;

此外,客戶端的監(jiān)視鏈表中并不是單單保存鍵,而是保存一個(gè)watchedKey結(jié)構(gòu),其中記錄著監(jiān)視的鍵和鍵所在的數(shù)據(jù)庫(kù)

//multi.c /* 客戶端監(jiān)視鏈表中保存的結(jié)構(gòu),記錄監(jiān)視的鍵和鍵所在的數(shù)據(jù)庫(kù) */ typedef struct watchedKey {robj *key;redisDb *db; } watchedKey;

監(jiān)視功能的實(shí)現(xiàn)

添加監(jiān)視的鍵

監(jiān)視功能由watchCommand函數(shù)實(shí)現(xiàn),函數(shù)中主要是將WATCH命令的參數(shù)依次添加到數(shù)據(jù)庫(kù)的監(jiān)視字典和客戶端的監(jiān)視鏈表中,由watchForKey函數(shù)完成

//multi.c /* 對(duì)鍵key進(jìn)行監(jiān)聽,需要更新數(shù)據(jù)庫(kù)的監(jiān)視字典和客戶端的監(jiān)視鏈表 */ void watchForKey(client *c, robj *key) {list *clients = NULL;listIter li;listNode *ln;watchedKey *wk;/* 對(duì)客戶端的監(jiān)視鏈表進(jìn)行遍歷,判斷鍵key是否已經(jīng)被監(jiān)視 */listRewind(c->watched_keys,&li);while((ln = listNext(&li))) {/* 取出鏈表的節(jié)點(diǎn) */wk = listNodeValue(ln);/* 判斷是否監(jiān)視過(guò)鍵key */if (wk->db == c->db && equalStringObjects(key,wk->key))return; /* Key already watched */}/* 從數(shù)據(jù)庫(kù)的監(jiān)視字典中取出鍵key對(duì)應(yīng)的客戶端鏈表,如果不存在,則創(chuàng)建一個(gè) */clients = dictFetchValue(c->db->watched_keys,key);if (!clients) {/* 不存在客戶端鏈表(當(dāng)前沒(méi)有客戶端對(duì)該鍵進(jìn)行監(jiān)聽),創(chuàng)建一個(gè)客戶端鏈表作為鍵key對(duì)應(yīng)的值 */clients = listCreate();dictAdd(c->db->watched_keys,key,clients);incrRefCount(key);}/* 將當(dāng)前客戶端追加到客戶端鏈表中 */listAddNodeTail(clients,c);/* 增加鍵key到客戶端的監(jiān)視鏈表中 */wk = zmalloc(sizeof(*wk));wk->key = key;wk->db = c->db;incrRefCount(key);listAddNodeTail(c->watched_keys,wk); }

修改被監(jiān)視的鍵對(duì)事務(wù)的影響

當(dāng)客戶端開始監(jiān)視功能后,其他客戶端任何對(duì)監(jiān)視鍵的修改都會(huì)破壞當(dāng)前客戶端的事務(wù)狀態(tài),導(dǎo)致Redis不再執(zhí)行這次的事務(wù)。所以在對(duì)鍵進(jìn)行修改的命令中一定有對(duì)監(jiān)視鍵的處理,以SET命令為例,可以看到在setKey函數(shù)中執(zhí)行了signalModifiedKey函數(shù),目的是將所有監(jiān)視該鍵的客戶端的事務(wù)狀態(tài)標(biāo)記為已破壞(Redis不會(huì)執(zhí)行已破壞的事務(wù))

//db.c /* 添加或覆蓋鍵值對(duì) */ void setKey(redisDb *db, robj *key, robj *val) {if (lookupKeyWrite(db,key) == NULL) {dbAdd(db,key,val);} else {dbOverwrite(db,key,val);}incrRefCount(val);removeExpire(db,key);/* 因?yàn)閷?duì)鍵key進(jìn)行了修改,所以會(huì)導(dǎo)致監(jiān)聽該鍵的客戶端事務(wù)被破壞* 調(diào)用該函數(shù)更改這些客戶端的事務(wù)狀態(tài) */signalModifiedKey(db,key); }

signalModifiedKey又調(diào)用touchWatchedKey函數(shù),完成實(shí)際的修改任務(wù)。該函數(shù)將所有監(jiān)視該鍵的客戶端的事務(wù)標(biāo)志設(shè)置為已破壞,當(dāng)客戶端輸入EXEC命令時(shí),Redis會(huì)先判斷事務(wù)狀態(tài),如果已破壞,則不再執(zhí)行事務(wù)隊(duì)列中的命令

//multi.c /* 掃描服務(wù)器的監(jiān)視字典,檢查鍵key是否被某些客戶端監(jiān)視,如果有,將對(duì)應(yīng)客戶端標(biāo)記為事務(wù)破壞狀態(tài) */ void touchWatchedKey(redisDb *db, robj *key) {list *clients;listIter li;listNode *ln;/* 如果數(shù)據(jù)庫(kù)中沒(méi)有鍵被監(jiān)視,則返回 */if (dictSize(db->watched_keys) == 0) return;/* 嘗試從監(jiān)視字典中取出鍵key對(duì)應(yīng)的值 */clients = dictFetchValue(db->watched_keys, key);/* 如果不存在,說(shuō)明沒(méi)有客戶端監(jiān)視該鍵,直接返回 */if (!clients) return;/* 設(shè)置迭代器方向從頭到尾,開始遍歷監(jiān)視鍵key的所有客戶端 */listRewind(clients,&li);while((ln = listNext(&li))) {/* 取出節(jié)點(diǎn)對(duì)應(yīng)的值 */client *c = listNodeValue(ln);/* 設(shè)置客戶端的事務(wù)狀態(tài),表示該客戶端的事務(wù)已經(jīng)被破壞,* 如果該客戶端使用EXEC執(zhí)行事務(wù),則什么也不做直接返回 */c->flags |= CLIENT_DIRTY_CAS;} }

在EXEC命令處理函數(shù)中,可以看到對(duì)于事務(wù)狀態(tài)的判斷

/* 啟動(dòng)事務(wù)命令 */ void execCommand(client *c) {.../* CLIENT_DIRTY_CAS標(biāo)識(shí)代表客戶端監(jiān)視的鍵是否被修改過(guò)* 如果被修改過(guò),說(shuō)明事務(wù)已被破壞,那么執(zhí)行事務(wù)就不再安全,直接返回 */if (c->flags & (CLIENT_DIRTY_CAS|CLIENT_DIRTY_EXEC)) {addReply(c, c->flags & CLIENT_DIRTY_EXEC ? shared.execaborterr :shared.nullmultibulk);discardTransaction(c);goto handle_monitor;}/* 開始執(zhí)行事務(wù),監(jiān)視任務(wù)就可以結(jié)束了,將該客戶端的監(jiān)視字典清空 */unwatchAllKeys(c); ... }

可以看到,在EXEC命令處理函數(shù)中有unwatchAllKeys這樣一個(gè)函數(shù)調(diào)用,原因是監(jiān)視功能只對(duì)單次事務(wù)有效,當(dāng)事務(wù)結(jié)束后,當(dāng)前客戶端所有的監(jiān)視也都會(huì)被清空。如果需要再進(jìn)行監(jiān)視,需要重新設(shè)置,當(dāng)然,這就涉及到下一輪的事務(wù)

小結(jié)

監(jiān)視功能和事務(wù)模塊是結(jié)合在一起的,如果想要對(duì)事務(wù)進(jìn)行保護(hù),確保當(dāng)其他客戶端修改了某些必要的鍵時(shí)取消事務(wù),就可以使用監(jiān)視功能。另外,UNWATCH命令用于取消對(duì)一個(gè)或多個(gè)鍵的監(jiān)視,不過(guò)該命令只能在事務(wù)之外執(zhí)行,如果在事務(wù)狀態(tài)中使用,則會(huì)被添加到事務(wù)隊(duì)列中

總結(jié)

以上是生活随笔為你收集整理的Redis源码剖析(七)监视功能的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。