? ? ? redis作為一非關系型數據庫,竟然同樣擁有與RDBMS的事務操作,不免讓我覺得比較驚訝。在redis就專門有文件就是執行事務的相關操作的。也可以讓我們領略一下,在Redis的代碼中是如何實現事務操作。首先亮出mulic.c下面的一些API。
~~~
/* ================================ 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的命令方法 */
~~~
方法不是很多,但是里面出現了一個出現頻率很高的詞"key"。這個key在這里的確是起到了關鍵的作用。在muli的代碼中主要包含了一些,加入命令,執行命令,還有一些撤銷指令的操作,比如下面的撤銷事務的操作。
~~~
/* 撤銷事務 */
void discardTransaction(redisClient *c) {
freeClientMultiState(c);
initClientMultiState(c);
c->flags &= ~(REDIS_MULTI|REDIS_DIRTY_CAS|REDIS_DIRTY_EXEC);
//客戶端取消監聽所有的key
unwatchAllKeys(c);
}
~~~
里面有個unwatchAllKeys()的方法。下面是事務操作的關鍵原理了:
~~~
/* 在事務處理中,存在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" 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;
}
}
~~~
當客戶端嘗試用touch的方法去監聽key的時候,Client的flag狀態唄改為了DIRTY_CAS,不禁讓我猜測,同步的方法是用CAS算法嘛,如果很多客戶端都在用此算法,的確挺耗CPU的哦。總的來說,key維護了一個Client列表,一個Client同樣擁有它所有watch的key列表,key的結構體很簡單:
~~~
/* 定義了watchedKey結構體 */
typedef struct watchedKey {
robj *key;
redisDb *db;
} watchedKey;
~~~
key包含了它所屬于的哪個數據庫,所以剛剛撤銷事務的操作,就要把客戶端所監聽的key都給移除掉了。
- 前言
- (一)--Redis結構解析
- (二)--結構體分析(1)
- (三)---dict哈希結構
- (四)-- sds字符串
- (五)--- sparkline微線圖
- (六)--- ziplist壓縮列表
- (七)--- zipmap壓縮圖
- (八)--- t_hash哈希轉換
- (九)--- t_list,t_string的分析
- (十)--- testhelp.h小型測試框架和redis-check-aof.c日志檢測
- (十一)--- memtest內存檢測
- (十二)--- redis-check-dump本地數據庫檢測
- (十三)--- redis-benchmark性能測試
- (十四)--- rdb.c本地數據庫操作
- (十五)--- aof-append only file解析
- (十六)--- config配置文件
- (十七)--- multi事務操作
- (十八)--- db.c內存數據庫操作
- (十九)--- replication主從數據復制的實現
- (二十)--- ae事件驅動
- (二十一)--- anet網絡通信的封裝
- (二十二)--- networking網絡協議傳輸
- (二十三)--- CRC循環冗余算法和RAND隨機數算法
- (二十四)--- tool工具類(2)
- (二十五)--- zmalloc內存分配實現
- (二十六)--- slowLog和hyperloglog
- (二十七)--- rio系統I/O的封裝
- (二十八)--- object創建和釋放redisObject對象
- (二十九)--- bio后臺I/O服務的實現
- (三十)--- pubsub發布訂閱模式
- (三十一)--- latency延遲分析處理
- (三十二)--- redis-cli.c客戶端命令行接口的實現(1)
- (三十三)--- redis-cli.c客戶端命令行接口的實現(2)
- (三十四)--- redis.h服務端的實現分析(1)
- (三十五)--- redis.c服務端的實現分析(2)
- (三十六)--- Redis中的11大優秀設計