1. 程式人生 > >二、redis系列之事務

二、redis系列之事務

1. 緒言

  Redis也提供了事務機制,可以一次執行多個命令,本質是一組命令的集合。一個事務中的所有命令都會序列化,按順序地序列化執行而不會被其他命令插入,不許加塞。但Redis對事務的支援是部分支援,不想關係型資料庫,要麼都成功要麼都失敗,Redis可以部分成功部分失敗。本篇中,我們來詳細所以說redis那些事。

2. Redis事務機制

2.1 事務流程

  Redis中的事務(transaction)是一組命令的集合。事務同命令一樣都是Redis最小的執行單位,一個事務中的命令要麼都執行,要麼都不執行,要麼都成功,要麼都失敗。Redis事務的實現需要用到 MULTI 和 EXEC 兩個命令,事務開始的時候先向Redis伺服器傳送 MULTI 命令,然後依次傳送需要在本次事務中處理的命令,最後再發送 EXEC 命令表示事務命令結束。一個事務從開始到執行會經歷以下三個階段:

  1)開始事務;

  2)命令入隊;

  3)執行事務。

   

  從上圖輸出中可以看到,當輸入MULTI命令後,伺服器返回OK表示事務開始成功,然後依次輸入需要在本次事務中執行的所有命令,每次輸入一個命令伺服器並不會馬上執行,而是返回”QUEUED”,這表示命令已經被伺服器接受並且暫時儲存起來,最後輸入EXEC命令後,本次事務中的所有命令才會被依次執行,可以看到最後伺服器一次性返回了三個OK,這裡返回的結果與傳送的命令是按順序一一對應的,這說明這次事務中的命令全都執行成功了。

2.2    事務命令

  在上一小節中,我們使用了MULTI命令和EXEC命令。MULTI命令標誌著事務的開始,EXEC命令開始執行事務。從上一小節圖片中我們也可以看出,事務中的命令要全部執行完之後才能獲取每個命令的結果,但是如果一個事務中的命令B依賴於他上一個命令A的結果的話該怎麼辦呢?例如電商系統在搶購業務中,先要獲取當前庫存,才能在當前庫存的基礎上進行其他操作。這種場合僅僅使用上面介紹的MULTI和EXEC是不能實現的,因為MULTI和EXEC中的命令是一起執行的,並不能將其中一條命令的執行結果作為另一條命令的執行引數。這時候就要用到redis事務機制中的其他命令,下面列出了redis事務機制中所有命令:

命令原型

時間複雜度

命令描述

返回值

MULTI

 

用於標記事務的開始,其後執行的命令都將被存入命令佇列,直到執行EXEC時,這些命令才會被原子的執行。

始終返回OK

EXEC

 

執行在一個事務內命令佇列中的所有命令,同時將當前連線的狀態恢復為正常狀態,即非事務狀態。如果在事務中執行了WATCH命令,那麼只有當WATCH所監控的Keys沒有被修改的前提下,EXEC命令才能執行事務佇列中的所有命令,否則EXEC將放棄當前事務中的所有命令。

原子性的返回事務中各條命令的返回結果。如果在事務中使用了WATCH,一旦事務被放棄,EXEC將返回NULL-multi-bulk回覆。

DISCARD

 

回滾事務佇列中的所有命令,同時再將當前連線的狀態恢復為正常狀態,即非事務狀態。如果WATCH命令被使用,該命令將UNWATCH所有的Keys。

始終返回OK。

WATCH key [key ...]

O(1)

在MULTI命令執行之前,可以指定待監控的Keys,然而在執行EXEC之前,如果被監控的Keys發生修改,EXEC將放棄執行該事務佇列中的所有命令。

始終返回OK。

UNWATCH

O(1)

取消當前事務中指定監控的Keys,如果執行了EXEC或DISCARD命令,則無需再手工執行該命令了,因為在此之後,事務中所有被監控的Keys都將自動取消。

始終返回OK。


  下面通過程式碼嘗試使用上述幾個命令: 

  1)正常執行

127.0.0.1:6379> MULTI

OK

127.0.0.1:6379> SET key01 a

QUEUED

127.0.0.1:6379> SET key02 b

QUEUED

127.0.0.1:6379> GET key01

QUEUED

127.0.0.1:6379> SET key03 c

QUEUED

127.0.0.1:6379> EXEC

1) OK

2) OK

3) "a"

4) OK

  2) 取消事務

127.0.0.1:6379> MULTI

OK

127.0.0.1:6379> SET key01 a

QUEUED

127.0.0.1:6379> SET key02 b

QUEUED

127.0.0.1:6379> DISCARD

OK

127.0.0.1:6379> GET key01

(nil)

  可以看到,執行DISCARD命令後,返回了OK,事務被取消,所以再次GET key01的時候返回了nil。

  3)WATCH

   

  命令按圖示箭頭方向順序輸入並執行,在左側視窗中用WATCH命令監視key01,然後MULTI命令開始後,在右側視窗更改了key02的值,所以左側視窗執行EXEC命令後,返回nil,事務執行失敗,事務中的INCR key01 , SET key02 1兩條命令都沒有執行,所以最後獲取key02返回的值是nil,而key01的值也是右側視窗的賦值。

  4)UNWATCH

   

  按圖示箭頭順序輸入並執行命令,WATCH監視key01後,用UNWATCH接觸監視,開始MULTI事務後,在右側視窗改變key01的值,然後左側視窗繼續執行事務,發現事務正常執行,事務中獲取到的key01的值是在右側視窗賦值的基礎上加1,key02也成功建立。

2.3    Redis事務中的錯誤

  先來看如下兩塊程式碼:

  程式碼一:

   

  在上述程式碼塊中,先給key01賦一個字串值,然後在事務中進行整數運算,顯然是有誤的,但是整個事務除了數值運算那個命令其他命令都成功執行。

  程式碼塊二:

   

  在上述程式碼塊中,事務中出現拼寫錯誤,執行事務後,直接提示失敗,沒有任何返回值,可以發現,事務中所有命令都沒有執行。

  對比上述兩個程式碼塊,為什麼一個事務成功執行,一個事務執行失敗呢?這就涉及到redis事務中的兩類失敗:

  1執行錯誤: 執行錯誤表示命令在執行過程中出現錯誤,比如用GET命令獲取一個散列表型別的鍵值、對字元型進行數字運算等。這種錯誤在命令執行之前Redis是無法發現的,所以在事務裡這樣的命令會被Redis接受並執行。如果事務裡有一條命令執行錯誤,其他命令依舊會執行(包括出錯之後的命令)。

  2)語法錯誤就像上面的例子一樣,語法錯誤表示命令不存在或者引數錯誤例如引數的數量錯誤、命令名稱錯誤,這種情況需要區分Redis的版本,Redis 2.6.5之前的版本會忽略錯誤的命令,執行其他正確的命令,2.6.5之後的版本會忽略這個事務中的所有命令,都不執行,就比如上面的例子。這種錯誤會導致事務執行失敗,事務中所有命令都執行失敗。

3. 總結

  本篇介紹了redis中的事務機制,但關於分散式鎖的部分並未涉及,之後再補充。

  參考:

  https://www.cnblogs.com/Jason-Xiang/p/5364252.html

  https://blog.csdn.net/qq_37169817/article/details/78839774