Redis的事務
概述
可以以此執行多個命令,本質上是一組命令的集合。一個事務中的所有命令多會序列化,按順序地串行化執行而不會被其他命令插入,不許加塞。
簡單地說,就是將一系列的命令按順序地加入到一個隊列中,一次性地、順序地、排他地執行這些命令。
常用命令
使用
步驟大致爲:multi(開啓事務)–>一系列的命令–>exec/discard(執行事務或放棄執行事務)
正常執行
## 開啓事務
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k1
QUEUED
## 執行事務
127.0.0.1:6379> EXEC
1) OK
2) OK
3) "v1"
可以發現,一系列的命令只是入隊QUEUED,而並沒有立即執行,而是等待EXEC時才執行
取消事務
## 開啓事務
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> get k4
QUEUED
## 取消事務
127.0.0.1:6379> DISCARD
OK
## 由於事務被取消,命令未被執行 所以k4沒有值
127.0.0.1:6379> get k4
(nil)
注意:redis統一事務中執行若干命令,並不能保證原子性
-
類比Java異常,如果是檢查異常,在編碼中未拋出時,系統會報錯。同理,在redis事務中,某個命令由於redis語法錯誤執行失敗,那麼該事務下的所有命令均執行失敗。
127.0.0.1:6379> FLUSHDB OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set k1 v1 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED ## 語法錯誤 拋出異常信息 127.0.0.1:6379> set k3 (error) ERR wrong number of arguments for 'set' command ## 執行事務時發現有錯,事務被取消 127.0.0.1:6379> EXEC (error) EXECABORT Transaction discarded because of previous errors. ## 其他正常命令的執行也被取消 127.0.0.1:6379> get k2 (nil) 127.0.0.1:6379> get k1 (nil)
-
類比Java編譯時異常,在redis事務中,某個命令因爲參數不合法等問題導致了錯誤,那麼該事務下只會影響當前命令的執行結果,不會影響其他命令的正常執行。
127.0.0.1:6379> FLUSHDB OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set k1 abcd QUEUED ## 參數不合法 字符串不能執行+1操作 但是這屬於編譯異常 這裏系統未檢出錯誤 直接入隊 127.0.0.1:6379> INCR k1 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> get k1 QUEUED 127.0.0.1:6379> EXEC 1) OK ## 執行時報錯參數問題,但是沒有影響其他命令的正常執行 2) (error) ERR value is not an integer or out of range 3) OK 4) "abcd" 127.0.0.1:6379> get k1 "abcd" 127.0.0.1:6379> get k2 "v2"
watch監控
watch監控類似於樂觀鎖的機制,監控了某key,在事務中真正執行時發現該key可能被其他客戶端修改了,那麼當前事務執行失敗。需要重新監控該key,直至監控時候key的數據跟執行事務時key的數據均一致方可
悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿數據的時候都認爲別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到它拿到鎖。傳統的關係型數據庫裏邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。
樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿數據的時候都認爲別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號等機制。樂觀鎖適用於多讀的應用類型,這樣可以提高吞吐量,
樂觀鎖策略:提交版本必須大於記錄當前版本才能執行更新
正常使用
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
## 同時監控兩個key
127.0.0.1:6379> WATCH k1 k2
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v11
QUEUED
127.0.0.1:6379> set k2 v22
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) OK
3) "v11"
4) "v22"
異常情況
模擬監控key後,key被其他客戶端修改。
說明:監控了兩個key,分別是k1,k2。但是在事務執行時,k1的值被其他客戶端修改過,k1的值相當於watch時被修改過。因此事務執行失敗。響應結果nil。
這種情況,只能重新watch,力求key是最新的數據。比如下面這次 就執行成功了
127.0.0.1:6379> WATCH k1 k2
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v11
QUEUED
127.0.0.1:6379> set k2 v22
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) OK
3) "v11"
unwatch
取消監控,同樣地,一旦事務執行完成,之前加到事務上某key的監控鎖也會被取消掉。
說明:監控了k1,後來又取消對k1的監控了。在事務執行前,對k1的值進行修改,對事務的正常執行沒有影響,k1的值變爲v11。因爲redis是單線程的,這裏推斷k1值的變化是:v1->v01->v11
watch總結
watch指令類似於樂觀鎖,事務提交時,如果key的值已被別的客戶端改變,比如某key被別的客戶端修改過,那麼整個事務隊列都不會被執行。
通過WATCH命令在事務執行之前監控了多個Keys(如上面的例子),倘若在WATCH之後有任何Key的值發生了變化,EXEC命令執行的事務都將被放棄,同時返回nil應答以通知調用者事務執行失敗。
總結
事務中的所有命令都會序列化、按順序地執行。事務在執行的過程中,不會被其他客戶端發送來的命令請求所打斷。
事務隊列中的命令沒有提交之前都不會實際的被執行,所以也就不會有隔離級別的問題。
不保證嚴格上的原子性操作。