第四部分 獨(dú)立功能的實(shí)現(xiàn)
Redis設(shè)計(jì)與實(shí)現(xiàn)簡(jiǎn)讀筆記鏈接
第一章_數(shù)據(jù)結(jié)構(gòu)與對(duì)象
第二章_單機(jī)數(shù)據(jù)庫(kù)的實(shí)現(xiàn)
第三章__多機(jī)數(shù)據(jù)庫(kù)的實(shí)現(xiàn)
發(fā)布訂閱
兩種類(lèi)型的訂閱
- 訂閱頻道
- 訂閱模式
SUBSCRIBE "news.it" //訂閱頻道
SUBSCRIBE "news.[ie]t" //訂閱模式
PUBLISH "news.it" "hello" //發(fā)布消息
頻道訂閱的數(shù)據(jù)結(jié)構(gòu)
#使用字典保存所有頻道的訂閱信息
struct redisServer{
//保存所有頻道的訂閱信息
dict *pubsub_channels;
}
模式訂閱的數(shù)據(jù)結(jié)構(gòu)
#使用鏈表保存所有模式的訂閱信息
struct redisServer{
//保存所有模式的訂閱信息
list *pubsub_patterns;
}
struct pubsubPattern{
//訂閱模式的客戶(hù)
redisClient *client;
//被訂閱的模式
robj *pattern;
}pubsubPattern;
頻道和模式的訂閱和退訂過(guò)程
無(wú)非就是在數(shù)據(jù)結(jié)構(gòu)上進(jìn)行增加,刪除節(jié)點(diǎn)
發(fā)送消息
- 將消息發(fā)送給頻道訂閱者
- 在頻道字典表中找出對(duì)應(yīng)的頻道
- 遍歷鏈表,將消息發(fā)送給所有的訂閱者
- 將消息發(fā)送給模式訂閱者
- 遍歷模式訂閱鏈表
- 如果頻道和模式匹配,則將消息發(fā)送給訂閱該模式的客戶(hù)端
幾個(gè)常用的命令
-
PUBSUB CHANNELS [pattern]
-
用于返回服務(wù)器當(dāng)前被訂閱的頻道
若給定pattern參數(shù), 則還需從被訂閱頻道列表中過(guò)濾 與pattern匹配 的頻道
-
-
PUBSUB NUMSUB [channel_1,channel_2....]
- 返回給定頻道的訂閱者數(shù)量
-
PUBSUB NUMPAT
- 返回服務(wù)器當(dāng)前被訂閱模式的數(shù)量
回顧
- 服務(wù)器狀態(tài)在pubsub_channels字典保存了所有頻道的訂閱信息: SUBSCRIBE命令負(fù)責(zé)將客戶(hù)端和被訂閱的頻道關(guān)聯(lián)到字典中,而UNSUBSCRIBE命令則負(fù)責(zé)解除客戶(hù)端和被退訂頻道的關(guān)聯(lián)
- 服務(wù)器狀態(tài)在pubsub_patterns鏈表保存了所有頻道的訂閱信息: PSUBSCRIBE命令負(fù)責(zé)將客戶(hù)端和被訂閱的模式關(guān)聯(lián)到這個(gè)鏈表中,而PUNSUBSCRIBE命令則負(fù)責(zé)解除客戶(hù)端和被退訂模式的關(guān)聯(lián)
- PUBLISH命令通過(guò)訪問(wèn)pubsub_channels字典來(lái)向頻道的所有訂閱者發(fā)送消息,而通過(guò)pubsub_patterns鏈表來(lái)向所有匹配頻道模式的訂閱者發(fā)送消息
- PUBSUB命令的幾個(gè)常用命令都是通過(guò)讀取頻道的字典和模式的鏈表信息來(lái)實(shí)現(xiàn)的.
事務(wù)
事務(wù)的實(shí)現(xiàn)
事務(wù)從開(kāi)始到結(jié)束所經(jīng)歷的幾個(gè)階段
- 事務(wù)開(kāi)始
- 事務(wù)入隊(duì)
- 事務(wù)執(zhí)行
事務(wù)開(kāi)始
MULTI
def MULTI();
#打開(kāi)事務(wù)標(biāo)志
client.flags |= REDIS_MULTI
#返回OK回復(fù)
事務(wù)入隊(duì)
表示事務(wù)狀態(tài)的數(shù)據(jù)結(jié)構(gòu)
typedef struct redisClient{
//事務(wù)狀態(tài)
multiState mstate ;
}
typedef struct multiState{
//事務(wù)隊(duì)列,FIFO順序
multiCmd *commands;
// 已入隊(duì)命令計(jì)數(shù)
int count
}multiState;
typedef struct multiCmd{ //記錄命令相關(guān)信息
//命令參數(shù)
robj **argv
//參數(shù)數(shù)量
int argc;
//命令指針
struct redisCommand *cmd
}multiCmd;
結(jié)構(gòu)圖
事務(wù)執(zhí)行
EXEC
執(zhí)行事務(wù)的偽代碼
def execute_transaction():
# 創(chuàng)建空白的回復(fù)隊(duì)列
reply_queue = []
# 取出事務(wù)隊(duì)列里的所有命令歧强、參數(shù)和參數(shù)數(shù)量
for cmd, argv, argc in client.transaction_queue:
# 執(zhí)行命令澜薄,并取得命令的返回值
reply = execute_redis_command(cmd, argv, argc)
# 將返回值追加到回復(fù)隊(duì)列末尾
reply_queue.append(reply)
# 清除客戶(hù)端的事務(wù)狀態(tài)
clear_transaction_state(client)
# 清空事務(wù)隊(duì)列
clear_transaction_queue(client)
# 將事務(wù)的執(zhí)行結(jié)果返回給客戶(hù)端
send_reply_to_client(client, reply_queue)
watch命令的實(shí)現(xiàn)
WATCH命令是一個(gè)樂(lè)觀鎖
, 它可以在EXEC命令執(zhí)行之前,監(jiān)視任意數(shù)量的數(shù)據(jù)庫(kù)鍵,并在EXEC命令執(zhí)行時(shí),檢查被監(jiān)視的鍵是否至少有一個(gè)已經(jīng)被修改過(guò)了,如果是的話 , 服務(wù)器將拒絕執(zhí)行事務(wù),并向客戶(hù)端返回代表事務(wù)執(zhí)行失敗的空回復(fù)
案例
WATCH命令的數(shù)據(jù)結(jié)構(gòu)
typedef struct redisDB{
//正在被WATCH命令監(jiān)視的鍵
dict *watched_keys; //鍵為被監(jiān)視的數(shù)據(jù)庫(kù)鍵, 值為監(jiān)視該鍵的客戶(hù)端鏈表
}
WATCH機(jī)制的觸發(fā)
當(dāng)數(shù)據(jù)庫(kù)執(zhí)行修改命令之后,都會(huì)調(diào)用 touchWatchKey函數(shù)對(duì) watches_keys字典進(jìn)行檢查, 檢查是否有客戶(hù)端正在監(jiān)視剛剛被命令修改過(guò)的數(shù)據(jù)庫(kù)鍵, 如果有的話,那么touchWatchKey函數(shù)會(huì)將監(jiān)視被修改鍵的客戶(hù)端的REDIS_DIRTY_CAS
標(biāo)志打開(kāi), 表示該客戶(hù)端的事務(wù)安全性被破壞了.
判斷事務(wù)是否安全
當(dāng)服務(wù)端收到一個(gè)客戶(hù)端發(fā)來(lái)的EXEC命令時(shí),服務(wù)器會(huì)根據(jù)這個(gè)客戶(hù)端是否打開(kāi)了REDIS_DIRTY_CAS標(biāo)志來(lái)決定是否執(zhí)行事務(wù)
回顧
- 事務(wù)提供了一種將多個(gè)命令打包,然后一次性,有序地執(zhí)行的機(jī)制
- 多個(gè)命令會(huì)被入隊(duì)到事務(wù)隊(duì)列中,然后按先進(jìn)先出的順序執(zhí)行
- 事務(wù)在執(zhí)行過(guò)程中不會(huì)被中斷,當(dāng)事務(wù)隊(duì)列的所有命令都被執(zhí)行完畢之后,事務(wù)才會(huì)結(jié)束
- 帶有WATCH命令的事務(wù)會(huì)將客戶(hù)端和被監(jiān)視的鍵在數(shù)據(jù)庫(kù)的watched_keys字典中進(jìn)行關(guān)聯(lián),當(dāng)鍵被修改時(shí),程序會(huì)將所有監(jiān)視被修改鍵的客戶(hù)端的REDIS_DIRTY_CAS標(biāo)志打開(kāi)