獨立功能的實現
1.發佈與訂閱
Redis的發佈與訂閱功能由 publish,subscribe,psubscribe等命令組成
客戶端訂閱一個或多個頻道
> subcribe "news.it" "news.et" //訂閱兩個頻道
客戶端向頻道發送消息
> publish "news.it" "hello" //向news.it頻道發送消息hello
訂閱了news.it頻道的客戶端都會收到hello消息
客戶端訂閱一個或多個模式
> psubscribe "news.*" "book.*" //至關於訂閱了匹配這兩個模式中任意一個的全部頻道
客戶端退訂頻道
> unsubscribe "news.sport"
客戶端退訂模式
2.頻道訂閱原理
當客戶端執行subscribe命令訂閱頻道時,客戶端與頻道就創建了一種訂閱關係。該關係存儲在服務器狀態的 pubsub_channels 字典裏。
字典鍵爲訂閱的頻道;字典值爲一個鏈表,鏈表中記錄了全部訂閱該頻道的客戶端
訂閱頻道時,根據是不是該頻道的第一個訂閱者進行不一樣處理
一、是第一個訂閱者,建立一個頻道對應的鍵(是字符串對象),並將鍵值設置爲空鏈表結構,而後將客戶端添加到鏈表中。
二、不是第一個訂閱者,則已存在鍵,那麼直接添加到對應的值鏈表結構的末尾。
退訂頻道時,根據是不是最後一個訂閱者進行不一樣處理
一、是最後一個訂閱者,則刪除退訂客戶端後,繼續刪除頻道在字典中對應的鍵
二、不是最後一個訂閱者,則直接根據鍵找到對應的值鏈表結構,而後遍歷刪除退訂客戶端便可
模式訂閱原理
模式的訂閱關係保存在服務器狀態的 pubsub_patterns 鏈表中
pubsub_patterns鏈表的每一個節點都包含一個pubsubPattern結構
pubsubPattern結構包含pattern屬性和client屬性(這兩個屬性都是簡單字符串構成,或者說這兩個一塊兒就是一個字符串對象?)
訂閱模式
新建一個pubsubPattern結構,將其pattern屬性設置爲對應模式,client屬性設置爲訂閱者,而後添加到當前pubsub_patterns鏈表的表尾。
退訂模式
查找pattern屬性爲退訂模式,client屬性爲退訂者的pubsubPattern結構便可。
發送消息流程與原理
> publish <channel> <message> //channel爲頻道
一、將消息發送給全部訂閱該頻道的訂閱者
原理:在pubsub_channels字典中找到對應的鍵,而後按照對應的值鏈表結構中的客戶端依次發送消息
二、將消息發送給予該頻道相匹配的模式的訂閱者
原理:在pubsub_patterns鏈表中遍歷,找到全部匹配該頻道的模式(pattern屬性),並將消息發送給他們對應的客戶端(client屬性)
查看訂閱信息的pubsub的三個子命令
> pubsub channels [pattern] //查詢服務器當前被訂閱的全部頻道,[]內爲可選參數,可用於匹配限制
> pubsub numsub [channel1 channel2 ...] //返回這些頻道的訂閱者數量,即訂閱者鍵對應的值鏈表結構的長度
> pubsub numpat //返回服務器當前被訂閱的模式的數量,即返回pubsub_patterns鏈表的長度
3.事務(transaction)
概念:將一種或多個命令打包,而後一次性、按順序的統一執行。
相關命令
監聽事務中的數據庫鍵
> watch <key_name> //key_name爲要監聽的數據庫鍵,用於判斷數據庫鍵是否在事務過程當中被其餘客戶端修改
開啓事務
> multi
提交併執行事務
> exec
注意:事務中的多個命令被一次性發送給服務器,而不是一條一條發送,這種方式被稱爲pipeline。 pipeline 能夠一次性發送多條命令並在執行完後一次性將結果返回,能夠減小客戶端與服務器之間的網絡通訊次數從而提高性能,而且 pineline 基於隊列,而隊列的特色是先進先出,這樣就保證數據的順序性。
事務的實現
通常分爲三個階段
1.事務開始
使用multi命令,執行該命令的客戶端從非事務狀態切換到事務狀態
客戶端狀態的flags屬性會打開 REDIS_MULTI標識,以標誌該客戶端開啓事務,處於事務狀態
2.命令入隊
非事務狀態下,Redis客戶端發送的命令會被服務器直接執行
事務狀態下,Redis服務器會將客戶端發送的命令放入一個事務隊列,並返回QUEUE(exec,discard,watch,multi四個命令除外)
每一個客戶端有本身的事務狀態,該狀態保存在客戶端的mstate屬性裏(是一個multiState結構,內含一個FIFO事務隊列和一個計數器count)
事務隊列是一個multiCmd數組,每一個multiCmd元素都存儲了一個入隊命令的信息(函數指針,命令參數,參數數量)
count計數器則用來統計事務隊列的長度。
3.事務執行
客戶端發送 exec 命令後
服務器遍歷客戶端的事務隊列,單線程串行執行事務隊列中的事務,並將每一個命令的結果依次返回給客戶端
Watch命令
watch命令是一個樂觀鎖,在exec命令前執行,用來監視數據庫鍵在事務創建過程當中是否發生改變
watch命令字exec命令執行時,會檢查其監視的鍵是否發生了修改;若是發生修改,則服務器會拒絕執行事務。
底層實現
每一個Redis服務器都保存了一個 watched_keys 字典,
該字典的鍵爲被監視的數據庫鍵,值是一個鏈表結構,鏈表中記錄了全部監視該數據庫鍵的客戶端
原理實現
一旦服務器執行了對數據庫進行修改的命令(如SET,LPUSH,DEL,ZADD等),便會在執行後調用一個函數對 watched_keys字典進行檢查,查看被監視的鍵是否發生了修改;有,則將監視該數據庫鍵的全部客戶端的REDIS_DIRTY_CAS標識打開,以表示客戶端事務安全性被破壞。而執行exec命令時,會對客戶端的REDIS_DIRTY_CAS標識進行判斷,若是發現該標識已打開,則認爲事務不安全,並拒絕執行該事務。
Redis事務的ACID
原子性、一致性、隔離性、持久性
Redis事務總具備原子性、一致性、隔離性;特定狀況下也會具備持久性(AOF持久化模式下,且appendfsync設置爲always)
一致的概念:數據符合數據庫的定義和要求,不包含非法或無效的錯誤數據
Redis從三個方面維護一致性(錯誤檢測和設計來維護)
1.入隊錯誤
若命令不存在、格式不正確等,入隊過程會報錯,而服務器拒絕執行入隊過程當中出現錯誤的事務
2.執行錯誤
如命令正確可是操做鍵對象不符合等(SET操做列表等),出錯命令被服務器識別,並進行相應錯誤處理,但錯誤命令不會對數據庫進行任何修改
3.服務器停機
(1)RDB、AOF持久化維護
(2)若是找不到對應文件,或處於無持久化模式下,則重啓後數據庫爲空白,仍符合一致的要求
Redis經過單線程、事務隊列串行執行某個事務的命令,因此即便多個客戶端事務併發,仍然不會互相影響
要了解爲何大部分狀況沒法維護持久性(如RDB持久化,如appendfsync=everysec/no時的持久化爲何不能維護)
Redis事務和傳統關係型數據庫事務最大區別:
Redis不支持事務的回滾(roll back),執行事務命令期間,即便發現命令執行錯誤,整個事務也會繼續執行下去,直到將該客戶端的對應的事務隊列中全部命令執行完畢。(問題是這樣不就不知足原子性了嗎?Redis做者認爲這種狀況是存在編譯錯誤,實際生產中不會出現?)