標籤:原始碼 nil bsp sha har 分析 oid nbsp exec
Redis事務提供了一種將多個命令請求打包,然後一次性、按照順序地執行多個命令的機制,並且在事務執行的期間,伺服器不會中斷事務而去執行其他不在事務中的命令請求,它會把事務中所有的命令都執行完畢才會去執行其他的命令。
How
Redis中提供了multi、discard、exec、watch、unwatch這幾個命令來實現事務的功能。
Redis的事務始於multi命令,之後跟著要在事務中執行的命令,終於exec命令或者discard命令。加入事務中的所有命令會原子的執行,中間不會穿插執行其他沒有加入事務的命令。
multi、exec和discard
multi命令告訴Redis用戶端要開始一個事物,然後Redis會返回一個OK,接下來所有的命令Redis都不會立即執行,只會返回QUEUED結果,直到遇到了exec命令才會去執行之前的所有的命令,或者遇到了discard命令,會拋棄執行之前加入事務的命令。
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> get gender
(nil)
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name Slogen
QUEUED
127.0.0.1:6379> set gender male
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
127.0.0.1:6379> mget name gender
1) "Slogen"
2) "male"
watch
watch命令是Redis提供的一個樂觀鎖,可以在exec執行之前,監視任意數量的資料庫key,並在exec命令執行的時候,檢測被監視的key是否至少有一個已經被修改,如果是的話,伺服器將拒絕執行事務,並向用戶端返回代表事務執行失敗的空回複。
首先在client1執行下列命令:
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> watch name
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name slogen
QUEUED
127.0.0.1:6379> set gender male
QUEUED
127.0.0.1:6379> get name
QUEUED
這個時候client還沒有執行exec命令,接下來在client2下執行下面命令修改name:
127.0.0.1:6379> set name rio
OK
127.0.0.1:6379> get name
"rio"
接下來在client1下執行exec命令:
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get name
"rio"
從執行結果可以看到,在client1中執行exec命令的時候,Redis會檢測到name欄位已經被其他用戶端修改了,所以拒絕執行事務中所有的命令,直接返回nil表示執行失敗。這個時候擷取到的name的值還是在client2中設定的rio。
Why
multi
Redis的事務始於multi命令,那麼就從multi命令的原始碼開始分析。
當Redis接收到用戶端發送過來的命令之後會執行multiCommand()這個方法,這個方法在multi.c檔案中。
void multiCommand(client *c) {
// 1. 如果檢測到flags裡面已經包含了CLIENT_MULTI
// 表示對應client已經處於事務的上下文中,返回錯誤
if (c->flags & CLIENT_MULTI) {
addReplyError(c,"MULTI calls can not be nested");
return;
}
// 2. 開啟flags的CLIENT_MULTI標識
c->flags |= CLIENT_MULTI;
// 3. 返回ok,告訴用戶端已經成功開啟事務
addReply(c,shared.ok);
}
從原始碼中可以看到,multiCommand()主要完成下面三件事:
檢測發送multi命令的client是否已經處於事務中,如果是則直接返回錯誤。從這裡可以看到,Redis不支援事務嵌套執行。
給對應client的flags標誌位中增加MULTI_CLIENT標誌,表示已經進入事務中。
返回OK告訴用戶端已經成功開啟事務。
Redis 源碼學習之 Redis 事務Nosql