W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
一個(gè)事務(wù)從開(kāi)始到結(jié)束通常會(huì)經(jīng)歷以下三個(gè)階段:
本節(jié)接下來(lái)的內(nèi)容將對(duì)這三個(gè)階段進(jìn)行介紹, 說(shuō)明一個(gè)事務(wù)從開(kāi)始到結(jié)束的整個(gè)過(guò)程。
MULTI 命令的執(zhí)行標(biāo)志著事務(wù)的開(kāi)始:
redis> MULTI
OK
MULTI 命令可以將執(zhí)行該命令的客戶端從非事務(wù)狀態(tài)切換至事務(wù)狀態(tài), 這一切換是通過(guò)在客戶端狀態(tài)的 flags
屬性中打開(kāi) REDIS_MULTI
標(biāo)識(shí)來(lái)完成的, MULTI 命令的實(shí)現(xiàn)可以用以下偽代碼來(lái)表示:
def MULTI():
# 打開(kāi)事務(wù)標(biāo)識(shí)
client.flags |= REDIS_MULTI
# 返回 OK 回復(fù)
replyOK()
當(dāng)一個(gè)客戶端處于非事務(wù)狀態(tài)時(shí), 這個(gè)客戶端發(fā)送的命令會(huì)立即被服務(wù)器執(zhí)行:
redis> SET "name" "Practical Common Lisp"
OK
redis> GET "name"
"Practical Common Lisp"
redis> SET "author" "Peter Seibel"
OK
redis> GET "author"
"Peter Seibel"
與此不同的是, 當(dāng)一個(gè)客戶端切換到事務(wù)狀態(tài)之后, 服務(wù)器會(huì)根據(jù)這個(gè)客戶端發(fā)來(lái)的不同命令執(zhí)行不同的操作:
QUEUED
回復(fù)。服務(wù)器判斷命令是該入隊(duì)還是該立即執(zhí)行的過(guò)程可以用流程圖 IMAGE_ENQUEUE_OR_EXEC 來(lái)描述。
每個(gè) Redis 客戶端都有自己的事務(wù)狀態(tài), 這個(gè)事務(wù)狀態(tài)保存在客戶端狀態(tài)的 mstate
屬性里面:
typedef struct redisClient {
// ...
// 事務(wù)狀態(tài)
multiState mstate; /* MULTI/EXEC state */
// ...
} redisClient;
事務(wù)狀態(tài)包含一個(gè)事務(wù)隊(duì)列, 以及一個(gè)已入隊(duì)命令的計(jì)數(shù)器 (也可以說(shuō)是事務(wù)隊(duì)列的長(zhǎng)度):
typedef struct multiState {
// 事務(wù)隊(duì)列,F(xiàn)IFO 順序
multiCmd *commands;
// 已入隊(duì)命令計(jì)數(shù)
int count;
} multiState;
事務(wù)隊(duì)列是一個(gè) multiCmd
類型的數(shù)組, 數(shù)組中的每個(gè) multiCmd
結(jié)構(gòu)都保存了一個(gè)已入隊(duì)命令的相關(guān)信息, 包括指向命令實(shí)現(xiàn)函數(shù)的指針, 命令的參數(shù), 以及參數(shù)的數(shù)量:
typedef struct multiCmd {
// 參數(shù)
robj **argv;
// 參數(shù)數(shù)量
int argc;
// 命令指針
struct redisCommand *cmd;
} multiCmd;
事務(wù)隊(duì)列以先進(jìn)先出(FIFO)的方式保存入隊(duì)的命令: 較先入隊(duì)的命令會(huì)被放到數(shù)組的前面, 而較后入隊(duì)的命令則會(huì)被放到數(shù)組的后面。
舉個(gè)例子, 如果客戶端執(zhí)行以下命令:
redis> MULTI
OK
redis> SET "name" "Practical Common Lisp"
QUEUED
redis> GET "name"
QUEUED
redis> SET "author" "Peter Seibel"
QUEUED
redis> GET "author"
QUEUED
那么服務(wù)器將為客戶端創(chuàng)建圖 IMAGE_TRANSACTION_STATE 所示的事務(wù)狀態(tài):
0
位置上。1
位置上。2
位置上。3
位置上。當(dāng)一個(gè)處于事務(wù)狀態(tài)的客戶端向服務(wù)器發(fā)送 EXEC 命令時(shí), 這個(gè) EXEC 命令將立即被服務(wù)器執(zhí)行: 服務(wù)器會(huì)遍歷這個(gè)客戶端的事務(wù)隊(duì)列, 執(zhí)行隊(duì)列中保存的所有命令, 最后將執(zhí)行命令所得的結(jié)果全部返回給客戶端。
舉個(gè)例子, 對(duì)于圖 IMAGE_TRANSACTION_STATE 所示的事務(wù)隊(duì)列來(lái)說(shuō), 服務(wù)器首先會(huì)執(zhí)行命令:
SET "name" "Practical Common Lisp"
接著執(zhí)行命令:
GET "name"
之后執(zhí)行命令:
SET "author" "Peter Seibel"
再之后執(zhí)行命令:
GET "author"
最后, 服務(wù)器會(huì)將執(zhí)行這四個(gè)命令所得的回復(fù)返回給客戶端:
redis> EXEC
1) OK
2) "Practical Common Lisp"
3) OK
4) "Peter Seibel"
EXEC 命令的實(shí)現(xiàn)原理可以用以下偽代碼來(lái)描述:
def EXEC():
# 創(chuàng)建空白的回復(fù)隊(duì)列
reply_queue = []
# 遍歷事務(wù)隊(duì)列中的每個(gè)項(xiàng)
# 讀取命令的參數(shù),參數(shù)的個(gè)數(shù),以及要執(zhí)行的命令
for argv, argc, cmd in client.mstate.commands:
# 執(zhí)行命令,并取得命令的返回值
reply = execute_command(cmd, argv, argc)
# 將返回值追加到回復(fù)隊(duì)列末尾
reply_queue.append(reply)
# 移除 REDIS_MULTI 標(biāo)識(shí),讓客戶端回到非事務(wù)狀態(tài)
client.flags &= ~REDIS_MULTI
# 清空客戶端的事務(wù)狀態(tài),包括:
# 1)清零入隊(duì)命令計(jì)數(shù)器
# 2)釋放事務(wù)隊(duì)列
client.mstate.count = 0
release_transaction_queue(client.mstate.commands)
# 將事務(wù)的執(zhí)行結(jié)果返回給客戶端
send_reply_to_client(client, reply_queue)
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: