共計 3772 個字符,預計需要花費 10 分鐘才能閱讀完成。
這篇文章將為大家詳細講解有關 redis 事務的示例分析,丸趣 TV 小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
一: 事務實戰
具體到事務是什么,要保證什么。。。這個我想沒必要說了,先不管三七二十一,看一下 redis 手冊,領略下它的魔力。
1. multi,exec
還記得 sqlserver 是怎么玩的嗎?一般都是這樣的三個步驟,生成事務,產生命令,執行事務,對吧,而對應 redis 呢??multi 就是生成事務,然后輸入 redis 命令,最后用 exec 執行命令,就像下面這樣:
可以看到,我 set 完命令之后,反饋信息是 QUEUED,最后我再執行 exec,這些命令才會真正的執行,就是這么的簡單,一切執行的就是那么的順利,一點都不拖泥帶水,可能有些人說,其實事務中還有一個 rollback 操作,但好像在 redis 中沒有看到,很遺憾是 redis 中沒有 rollback 操作,比如下面這樣。
在圖中我故意用 lpush 命令去執行 string,可想而知自然不會執行成功,但從結果中,你看到什么了呢?兩個 OK,一個 Error,這就是違反了事務的原子性,但是我該怎么反駁呢??? reids 僅僅是個數據結構服務器,多簡單的一件事情,退一萬步說,很明顯的錯誤命令它會直接返回的,比如我故意把 lpush 寫成 lpush2:
2. watch
不知道你看完 multi 后面的三條 set 命令之后,有沒有一種心虛的感覺,怎么說呢,就是只要命令是正確的,redis 保證會一并執行,誓死完成任務,雖然說命令是一起執行的,但是誰可以保證我在執行命令的過程中,其他 client 不會修改這些值呢???如果修改了這些值,那我的 exec 還有什么意義呢???沒關系,這種爛大街的需求,redis 怎可能袖手旁觀???這里的 watch 就可以助你一臂之力。
WATCH
WATCH key [key ...]
監視一個 (或多個) key ,如果在事務執行之前這個 (或這些) key 被其他命令所改動,那么事務將被打斷。
上面就是 redis 手冊中關于 watch 的解釋,使用起來貌似很簡單,就是我在 multi 之前,用 watch 去監視我要修改的 key,如果說我在 exec 之前,multi 之后的這段時間,key 被其他 client 修改,那么 exec 就會執行失敗,返回(nil),就這么簡單,我還是來舉個例子:
二:原理探索
關于事務操作的源代碼,大多都在 redis 源碼中的 multi.c 文件中,接下來我會一個一個的簡單剖析一下:
1. multi
在 redis 的源代碼中,它大概是這么寫的:
void multiCommand(redisClient *c) { if (c- flags REDIS_MULTI) {
addReplyError(c, MULTI calls can not be nested
return;
}
c- flags |= REDIS_MULTI;
addReply(c,shared.ok);
從這段代碼中,你可以看到 multi 只是簡單的把 redisClient 的 REDIS_MULTI 狀態打開,告訴這個 redis 客戶端已經進入事務模式了。
2. 生成命令
在 redisClient 中,里面有一個 multiState 命令:
typedef struct redisClient {
。。。 multiState mstate; /* MULTI/EXEC state */
。。。} redisClient;
從注釋中你大概也看到了這個命令和 multi/exec 肯定有關系,接下來我很好奇的看看 multiState 的定義:
typedef struct multiState {
multiCmd *commands; /* Array of MULTI commands */
int count; /* Total number of MULTI commands */
int minreplicas; /* MINREPLICAS for synchronous replication */
time_t minreplicas_timeout; /* MINREPLICAS timeout as unixtime. */
} multiState;
從 multiState 這個枚舉中,你可以看到下面有一個 *command 命令,從注釋中可以看到它其實指向的是一個數組,它就是你的若干條命令啦。。。下面還有一個 count,可以看到是實際的 commands 的總數。
3. watch
為了方便說到后面的 exec,這里想說一下 watch 大概是怎么實現的,在 multi.c 源代碼中是這樣寫的。
typedef struct watchedKey {
robj *key;
redisDb *db;
} watchedKey;
void watchCommand(redisClient *c) {
int j;
if (c- flags REDIS_MULTI) {
addReplyError(c, WATCH inside MULTI is not allowed
return;
}
for (j = 1; j c- argc; j++)
watchForKey(c,c- argv[j]);
addReply(c,shared.ok);
}
/* Watch for the specified key */
void watchForKey(redisClient *c, robj *key) {
list *clients = NULL;
listIter li;
listNode *ln;
watchedKey *wk;
/* Check if we are already watching for this key */
listRewind(c- watched_keys, li);
while((ln = listNext( li))) { wk = listNodeValue(ln);
if (wk- db == c- db equalStringObjects(key,wk- key))
return; /* Key already watched */
}
/* This key is not already watched in this DB. Let s add it */
clients = dictFetchValue(c- db- watched_keys,key);
if (!clients) { clients = listCreate();
dictAdd(c- db- watched_keys,key,clients);
incrRefCount(key);
}
listAddNodeTail(clients,c);
/* Add the new key to the list of keys watched by this client */
wk = zmalloc(sizeof(*wk));
wk- key = key;
wk- db = c-
incrRefCount(key);
listAddNodeTail(c- watched_keys,wk);
}
這段代碼中大概最核心的一點就是:
/* This key is not already watched in this DB. Let s add it */
clients = dictFetchValue(c- db- watched_keys,key);
就是通過 dicFetchValue 這個字典方法,從 watched_keys 中找到指定 key 的 value,而這個 value 是一個 clients 的鏈表,說明人家其實是想找到關于這個 key 的所有 client,最后還會將本次 key 塞入到 redisclient 的 watched_keys 字典中,如下代碼:
/* Add the new key to the list of keys watched by this client */
wk = zmalloc(sizeof(*wk));
wk- key = key;
wk- db = c-
incrRefCount(key);
listAddNodeTail(c- watched_keys,wk);
如果非要畫圖,大概就是這樣:
其中 watched_key 是個字典結構,字典的鍵為上面的 key1,key2。。。,value 為 client 的鏈表,這樣的話,我就非常清楚某個 key 中是被哪些 client 監視著的。
4.exec
這個命令里面大概做了兩件事情:
1 : 判斷 c - flags=REDIS_DIRTY_EXEC 打開與否,如果是的話,取消事務 discardTransaction(c),也就是說這個 key 已經被別的 client 修改了。
2 : 如果沒有修改,那么就 for 循環執行 comannd[] 中的命令,如下圖中的兩處信息:
關于“redis 事務的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。