Redis原始碼分析(十七)--- multi事務操作
阿新 • • 發佈:2018-12-25
redis作為一非關係型資料庫,竟然同樣擁有與RDBMS的事務操作,不免讓我覺得比較驚訝。在redis就專門有檔案就是執行事務的相關操作的。也可以讓我們領略一下,在Redis的程式碼中是如何實現事務操作。首先亮出mulic.c下面的一些API。
方法不是很多,但是裡面出現了一個出現頻率很高的詞"key"。這個key在這裡的確是起到了關鍵的作用。在muli的程式碼中主要包含了一些,加入命令,執行命令,還有一些撤銷指令的操作,比如下面的撤銷事務的操作。/* ================================ MULTI/EXEC ============================== */ void initClientMultiState(redisClient *c) /* 初始化客戶端操作 */ void freeClientMultiState(redisClient *c) /* 釋放客戶端所有與multi/exec相關的資源 */ void queueMultiCommand(redisClient *c) /* 客戶端的multi命令佇列新增一條新的命令 */ void discardTransaction(redisClient *c) /* 撤銷事務操作 */ void flagTransaction(redisClient *c) /* 標記一個事物為DIRTY_EXEC狀態,最後這個事物會執行失敗,,此方法調用於插入命令的時候 */ void multiCommand(redisClient *c) /* 加入multi命令 */ void discardCommand(redisClient *c) /* 撤銷命令 */ void execCommandPropagateMulti(redisClient *c) /* 傳送multi命令給所有的從客戶端和aof檔案 */ void execCommand(redisClient *c) /* 客戶單執行Command命令 */ void watchForKey(redisClient *c, robj *key) /* 為客戶端新增key監聽 */ void unwatchAllKeys(redisClient *c) /* 客戶端移除所有的key */ void touchWatchedKey(redisDb *db, robj *key) /* touch key的意思,表示key正在被監聽,下一條執行操作將會失敗 */ void touchWatchedKeysOnFlush(int dbid) /* 根據key所在的的db,把此db下的watched-key統統touch一遍 */ void watchCommand(redisClient *c) /* watch key 的命令方法,通過client中的引數傳值 */ void unwatchCommand(redisClient *c) /* 取消監聽key的命令方法 */
裡面有個unwatchAllKeys()的方法。下面是事務操作的關鍵原理了:/* 撤銷事務 */ void discardTransaction(redisClient *c) { freeClientMultiState(c); initClientMultiState(c); c->flags &= ~(REDIS_MULTI|REDIS_DIRTY_CAS|REDIS_DIRTY_EXEC); //客戶端取消監聽所有的key unwatchAllKeys(c); }
/* 在事務處理中,存在2種mapping對映,key-->client lists ,表示所有列表中的Client都在監聽這個key
,當這個key的value發生改變了,可以標記這些Client為DIRTY狀態,需要更新了,同時在Client內部也會維護
一個key of list,表示一個客戶端所監視的所有key,當Client發生free操作等,就要把key裡面維護的Client列表
做更新*/
/* touch key的意思,表示key正在被監聽,下一條執行操作將會失敗 */
也就是說,正在客戶端正在監聽的key,他的下一步命令將會執行失敗,達到了同步的效果,
當客戶端嘗試用touch的方法去監聽key的時候,Client的flag狀態唄改為了DIRTY_CAS,不禁讓我猜測,同步的方法是用CAS演算法嘛,如果很多客戶端都在用此演算法,的確挺耗CPU的哦。總的來說,key維護了一個Client列表,一個Client同樣擁有它所有watch的key列表,key的結構體很簡單:/* "Touch" a key, so that if this key is being WATCHed by some client the * next EXEC will fail. */ /* touch key的意思,表示key正在被監聽,下一條執行操作將會失敗 */ void touchWatchedKey(redisDb *db, robj *key) { list *clients; listIter li; listNode *ln; if (dictSize(db->watched_keys) == 0) return; clients = dictFetchValue(db->watched_keys, key); if (!clients) return; /* Mark all the clients watching this key as REDIS_DIRTY_CAS */ /* Check if we are already watching for this key */ listRewind(clients,&li); while((ln = listNext(&li))) { redisClient *c = listNodeValue(ln); //遍歷該key擁有的Client,把flag標記為DIRTY_CAS狀態 c->flags |= REDIS_DIRTY_CAS; } }
/* 定義了watchedKey結構體 */
typedef struct watchedKey {
robj *key;
redisDb *db;
} watchedKey;
key包含了它所屬於的哪個資料庫,所以剛剛撤銷事務的操作,就要把客戶端所監聽的key都給移除掉了。