Redis系列(九):Redis的事務機制


提到事務,相信大家都不陌生,事務的ACID四大特性,也是面試時經常問的,不過一般情況下,我們可能想到的是傳統關系型數據庫的事務,其實,Redis也是提供了事務機制的,本篇博客就來講解下Redis的事務機制。

1. 事務演示

Redis的事務提供了一種將多個命令請求打包,然后一次性、按順序性地執行多個命令的機制。

在事務執行期間,服務器不會中斷事務而去執行其它客戶端的命令請求,它會將事務中的所有命令執行完畢,然后才去處理其它客戶端的命令請求。

下圖展示了一個Redis事務的執行過程:

可以看出,事務以MULTI命令開始,然后將多個命令放到事務當中,最后由EXEC命令將這個事務提交給服務器執行。

2. 事務實現原理

一個事務從開始到結束會經歷以下3個階段:

  1. 事務開始
  2. 命令入隊
  3. 事務執行

2.1 事務開始

MULTI命令的執行標志着事務的開始。

執行完該命令后,客戶端狀態的flags屬性會打開REDIS_MULTI標識,表示該客戶端從非事務狀態切換至事務狀態。

2.2 命令入隊

當一個客戶端處於非事務狀態時,這個客戶端發送的命令會立即被服務器執行:

當一個客戶端處於事務狀態時,這個客戶端發送的命令,服務器是否會立即執行,分為以下2種情況:

  1. 如果客戶端發送的命令為MULTIEXECWATCHDISCARD四個命令中的其中1個,服務器會立即執行這個命令。
  2. 如果客戶端發送的命令為以上4個命令外的其它命令,服務器不會立即執行這個命令,而是將其放到事務隊列里,然后向客戶端返回QUEUED回復。

以上流程可以使用以下流程圖來表示:

這里首先提下DISCARD命令,這個命令用於取消事務,放棄執行事務塊內的所有命令,如下所示:

然后提下事務隊列,每個Redis客戶端都有自己的事務狀態,事務狀態存儲在客戶端狀態的mstates屬性里:

事務狀態包含1個事務隊列和1個已入隊命令的數量,如下所示:

事務隊列是一個multiCmd類型的數組,數組中的每個multiCmd結構保存了一個已入隊命令的相關信息,比如:

  1. 指向命令實現函數的指針,如GET命令、SET命令
  2. 命令的參數
  3. 參數的數量

事務隊列以先進先出(FIFO)的方式保存入隊的命令。

2.3 事務執行

當一個處於事務狀態的客戶端執行EXEC命令時,服務器會遍歷這個客戶端的事務隊列,執行隊列中保存的所有命令(按先入先出順序),然后將執行命令的結果一次性返回給客戶端。

3. WATCH命令的實現原理

WATCH命令用於監視任意數量的數據庫鍵,並在EXEC命令執行時,檢測被監視的鍵是否被修改,如果被修改了,服務器將拒絕執行事務,並向客戶端返回空回復。

為了更好的理解,我們做個演示,首先,我們打開客戶端1,執行WATCH命令監視鍵“name”,然后開啟一個事務:

此時,先不要執行EXEC命令,打開客戶端2,執行以下命令修改“name”鍵的值:

然后,在客戶端1執行EXEC命令時,會返回空回復,因為“name”鍵的值在客戶端2已經被修改:

那么,WATCH命令的實現原理是什么樣的呢?我們從以下3個方面來分析:

  1. 使用WATCH命令監視數據庫鍵
  2. 監視機制的觸發
  3. 判斷事務是否安全

3.1 使用WATCH命令監視數據庫鍵

每個Redis數據庫都保存着1個watched_keys字典,這個字典的鍵是某個被WATCH命令監視的數據庫鍵,字典的值是一個鏈表,鏈表中記錄了所有監視相應數據庫鍵的客戶端。

舉個例子,假如客戶端1正在監視鍵“name”,客戶端2正在監視鍵“age”,那么watched_keys字典存儲的數據大概如下:

如果此時客戶端3執行了以下WATCH命令:

那么watched_keys字典存儲的數據就變為:

3.2 監視機制的觸發

那么問題來了,既然watched_keys字典存儲了被WATCH命令監視的鍵,那么監視機制是如何被觸發的呢?

答案是所有對數據庫修改的命令,比如SETLPUSHSADD等,在執行之后都會對watched_keys字典進行檢查,如果有客戶端正在監視剛剛被命令修改的鍵,那么所有監視該鍵的客戶端的REDIS_DIRTY_CAS標識將被打開,表示該客戶端的事務安全性已經被破壞。

以上圖為例,如果鍵“name”的值被修改,那么客戶端1、客戶端3的REDIS_DIRTY_CAS標識會被打開。

3.3 判斷事務是否安全

最后非常關鍵的一步是,當服務器接收到一個客戶端發來的EXEC命令時,服務器會根據這個客戶端是否打開了REDIS_DIRTY_CAS標識來決定是否執行事務,判斷的流程圖如下所示:

4. 事務執行失敗舉例

先來看第1個例子,這個事務因為命令入隊出錯被服務器拒絕執行,事務中的所有命令都不會被執行:

再來看第2個例子,事務入隊時出現了不存在的命令,服務器將拒絕執行這個事務:

再來看第3個例子,RPUSH命令在執行期間報錯了,但后續命令仍然繼續執行,並且之前執行的命令沒有受到任何影響:

這個例子也說明Redis事務不支持回滾機制

5. 總結

Redis的事務提供了一種將多個命令打包,然后一次性、有序地執行的機制,

它的原理是多個命令會被入隊到事務隊列中,然后按先進先出(FIFO)的順序執行,

並且事務在執行過程中不會被中斷,當事務隊列中的所有命令都被執行完畢之后,事務才會結束。

6. 參考

黃健宏 《Redis設計與實現》



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM