1. 程式人生 > 實用技巧 >Redis-事務

Redis-事務

1.1 為什麼要用事務

  我們知道 Redis 的單個命令是原子性的(比如 get set mget mset),如果涉及到多個命令的時候,需要把多個命令作為一個不可分割的處理序列,就需要用到事務。 例如我們之前說的用 setnx 實現分散式鎖,我們先 set,然後設定對 key 設定 expire,防止 del 發生異常的時候鎖不會被釋放,業務處理完了以後再 del,這三個動作我們就希 望它們作為一組命令執行。   Redis 的事務有兩個特點: 1、按進入佇列的順序執行。 2、不會受到其他客戶端的請求的影響。   Redis 的事務涉及到四個命令: multi(開啟事務),exec(執行事務),discard(取消事務),watch(監視)。

1.2 事務的用法

  案例場景:tom 和 mic 各有 1000 元,tom 需要向 mic 轉賬 100 元。tom 的賬戶餘額減少 100 元,mic 的賬戶餘額增加 100 元。
  127.0.0.1:6379> set tom 1000 
  OK
  127.0.0.1:6379> set mic 1000 
  OK
  127.0.0.1:6379> multi 
  OK
  127.0.0.1:6379> decrby tom 100 
  QUEUED 
  127.0.0.1:6379> incrby mic 100 
  QUEUED 
  127.0.0.1:6379> exec
  1) (integer) 900 
  2) (integer) 1100 
  127.0.0.1:6379> get tom 
"900" 127.0.0.1:6379> get mic
"1100"
  通過 multi 的命令開啟事務。事務不能巢狀,多個 multi 命令效果一樣。multi 執行後,客戶端可以繼續向伺服器傳送任意多條命令, 這些命令不會立即被執行, 而是被放到一個佇列中, 當 exec 命令被呼叫時, 所有佇列中的命令才會被執行。   通過 exec 的命令執行事務。如果沒有執行 exec,所有的命令都不會被執行。如果中途不想執行事務了,怎麼辦?可以呼叫 discard 可以清空事務佇列,放棄執行。
multi 
set k1 1 
set k2 2 
set k3 3 
discard

1.3 watch 命令

  在 Redis 中還提供了一個 watch 命令。它可以為 Redis 事務提供 CAS 樂觀鎖行為(Check and Set / Compare andSwap),也就是多個執行緒更新變數的時候, 會跟原值做比較,只有它沒有被其他執行緒修改的情況下,才更新成新的值。   我們可以用 watch 監視一個或者多個 key,如果開啟事務之後,至少有一個被監視key 鍵在 exec 執行之前被修改了, 那麼整個事務都會被取消(key 提前過期除外)。 可以用 unwatch 取消。

1.4 事務可能遇到的問題

  我們把事務執行遇到的問題分成兩種,一種是在執行 exec 之前發生錯誤,一種是在執行 exec 之後發生錯誤。

1.4.1 在執行 exec 之前發生錯誤

  比如:入隊的命令存在語法錯誤,包括引數數量,引數名等等(編譯器錯誤)。
127.0.0.1:6379> multi 
OK
127.0.0.1:6379> set gupao 666 
QUEUED 
127.0.0.1:6379> hset qingshan 2673 
(error) ERR wrong number of arguments for 'hset' command 
127.0.0.1:6379> exec 
(error) EXECABORT Transaction discarded because
在這種情況下事務會被拒絕執行,也就是佇列中所有的命令都不會得到執行。

1.4.2 在執行 exec 之後發生錯誤

  比如,型別錯誤,比如對 String 使用了 Hash 的命令,這是一種執行時錯誤。
127.0.0.1:6379> multi 
OK
127.0.0.1:6379> set k1 1 
QUEUED 
127.0.0.1:6379> hset k1 a b
QUEUED 
127.0.0.1:6379> exec 
1) OK 
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 
127.0.0.1:6379> get k1 
"1"
  最後我們發現 set k1 1 的命令是成功的,也就是在這種發生了執行時異常的情況下,只有錯誤的命令沒有被執行,但是其他命令沒有受到影響。 這個顯然不符合我們對原子性的定義,也就是我們沒辦法用 Redis 的這種事務機制來實現原子性,保證資料的一致。