簡讀筆記-Redis設計與實現第四章

你的頭髮真的好長發表於2019-05-02

第四部分 獨立功能的實現

Redis設計與實現簡讀筆記連結

釋出訂閱

兩種型別的訂閱

  • 訂閱頻道
  • 訂閱模式
SUBSCRIBE "news.it"		//訂閱頻道
SUBSCRIBE "news.[ie]t"	//訂閱模式
PUBLISH "news.it" "hello" //釋出訊息
複製程式碼

頻道和模式的訂閱狀態

頻道訂閱的資料結構

#使用字典儲存所有頻道的訂閱資訊
struct redisServer{
    //儲存所有頻道的訂閱資訊
    dict *pubsub_channels;
}
複製程式碼

頻道訂閱資料結構

模式訂閱的資料結構

#使用連結串列儲存所有模式的訂閱資訊
struct redisServer{
	//儲存所有模式的訂閱資訊
	list *pubsub_patterns;
}

struct pubsubPattern{
	//訂閱模式的客戶
	redisClient *client;
	//被訂閱的模式
	robj *pattern;
}pubsubPattern;
複製程式碼

模式訂閱資料結構

頻道和模式的訂閱和退訂過程

無非就是在資料結構上進行增加,刪除節點

傳送訊息

  • 將訊息傳送給頻道訂閱者
    • 在頻道字典表中找出對應的頻道
    • 遍歷連結串列,將訊息傳送給所有的訂閱者
  • 將訊息傳送給模式訂閱者
    • 遍歷模式訂閱連結串列
    • 如果頻道和模式匹配,則將訊息傳送給訂閱該模式的客戶端

幾個常用的命令

  • PUBSUB CHANNELS [pattern]

    • 用於返回伺服器當前被訂閱的頻道

      若給定pattern引數, 則還需從被訂閱頻道列表中過濾 與pattern匹配 的頻道

  • PUBSUB NUMSUB [channel_1,channel_2....]

    • 返回給定頻道的訂閱者數量
  • PUBSUB NUMPAT

    • 返回伺服器當前被訂閱模式的數量

回顧

  1. 伺服器狀態在pubsub_channels字典儲存了所有頻道的訂閱資訊: SUBSCRIBE命令負責將客戶端和被訂閱的頻道關聯到字典中,而UNSUBSCRIBE命令則負責解除客戶端和被退訂頻道的關聯
  2. 伺服器狀態在pubsub_patterns連結串列儲存了所有頻道的訂閱資訊: PSUBSCRIBE命令負責將客戶端和被訂閱的模式關聯到這個連結串列中,而PUNSUBSCRIBE命令則負責解除客戶端和被退訂模式的關聯
  3. PUBLISH命令通過訪問pubsub_channels字典來向頻道的所有訂閱者傳送訊息,而通過pubsub_patterns連結串列來向所有匹配頻道模式的訂閱者傳送訊息
  4. PUBSUB命令的幾個常用命令都是通過讀取頻道的字典和模式的連結串列資訊來實現的.

事務

事務的實現

事務從開始到結束所經歷的幾個階段

  • 事務開始
  • 事務入隊
  • 事務執行

事務開始

MULTI

def MULTI();
	#開啟事務標誌
	client.flags |= REDIS_MULTI
	#返回OK回覆
複製程式碼

事務入隊

事務入隊

表示事務狀態的資料結構

typedef struct redisClient{
    //事務狀態
    multiState mstate ; 	
}

typedef struct multiState{
    //事務佇列,FIFO順序
    multiCmd *commands;
    // 已入隊命令計數
    int count
}multiState;

typedef struct multiCmd{	//記錄命令相關資訊
    //命令引數
    robj **argv
    //引數數量
    int argc;
    //命令指標
    struct redisCommand *cmd
}multiCmd;
複製程式碼

結構圖

事務狀態的資料結構

事務執行

EXEC

執行事務的虛擬碼

def execute_transaction():

    # 建立空白的回覆佇列
    reply_queue = []

    # 取出事務佇列裡的所有命令、引數和引數數量
    for cmd, argv, argc in client.transaction_queue:

        # 執行命令,並取得命令的返回值
        reply = execute_redis_command(cmd, argv, argc)

        # 將返回值追加到回覆佇列末尾
        reply_queue.append(reply)

    # 清除客戶端的事務狀態
    clear_transaction_state(client)

    # 清空事務佇列
    clear_transaction_queue(client)

    # 將事務的執行結果返回給客戶端
    send_reply_to_client(client, reply_queue)
複製程式碼

watch命令的實現

WATCH命令是一個樂觀鎖 , 它可以在EXEC命令執行之前,監視任意數量的資料庫鍵,並在EXEC命令執行時,檢查被監視的鍵是否至少有一個已經被修改過了,如果是的話 , 伺服器將拒絕執行事務,並向客戶端返回代表事務執行失敗的空回覆

案例

簡讀筆記-Redis設計與實現第四章

WATCH命令的資料結構

typedef struct redisDB{
    //正在被WATCH命令監視的鍵
    dict *watched_keys;	//鍵為被監視的資料庫鍵, 值為監視該鍵的客戶端連結串列
}
複製程式碼

watched_keys字典

WATCH機制的觸發

當資料庫執行修改命令之後,都會呼叫 touchWatchKey函式對 watches_keys字典進行檢查, 檢查是否有客戶端正在監視剛剛被命令修改過的資料庫鍵, 如果有的話,那麼touchWatchKey函式會將監視被修改鍵的客戶端REDIS_DIRTY_CAS標誌開啟, 表示該客戶端的事務安全性被破壞了.

判斷事務是否安全

當服務端收到一個客戶端發來的EXEC命令時,伺服器會根據這個客戶端是否開啟了REDIS_DIRTY_CAS標誌來決定是否執行事務

伺服器判斷是否執行事務的過程

回顧

  • 事務提供了一種將多個命令打包,然後一次性,有序地執行的機制
  • 多個命令會被入隊到事務佇列中,然後按先進先出的順序執行
  • 事務在執行過程中不會被中斷,當事務佇列的所有命令都被執行完畢之後,事務才會結束
  • 帶有WATCH命令的事務會將客戶端和被監視的鍵在資料庫的watched_keys字典中進行關聯,當鍵被修改時,程式會將所有監視被修改鍵的客戶端的REDIS_DIRTY_CAS標誌開啟

相關文章