redis事務
multi exec discard 和watch 是redis事務的基礎
事務可以一次執行多個命令,並且帶有以下兩個重要的保證:
1 事務是一個單獨的隔離操作:事務中所有的命令都會序列化,按順序地執行。事務在執行的過程中,不會被其它來的命令請求打斷
2.事務是一個原子操作:事務中的命令要麼全部地執行,要麼全部都不執行
EXEC 命令負責觸發並執行事務中所有命令:
1.如果客戶端在使用multi 開啓一個事務之後,卻因爲斷線而沒有成功執行EXEC,那麼事務中所有命令都不會執行
2.另一方面,如果客戶端成功在開啓事務之後執行EXEC,那麼事務中所有命令都會執行
例如 購票過程,
ticket-1 ,mobey-100
而票只有一張,如果在multi之後,跟EXEC之前,被別人買了,所以ticket爲0
那我該如何監視這種情景,並不提交
悲觀的想法:覺得所有的人都在跟我搶這個ticket,此時給ticket上鎖,只有我能操作【悲觀鎖】
樂觀的想法:我只需要注意這個ticket的值就可以了【樂觀鎖】
redis的事務中就是開啓了樂觀鎖,只負責監聽ticket有沒有被改動
用法
multi 命令用戶開啓一個事務,它總是返回ok
multi 執行之後,客戶端可以繼續向服務端發送任意多條命令,這些命令不會立即執行,而是被放到一個隊列中,當需要調用時,所有隊列中的命令纔會被執行
另一方面,通過調用discard,客戶端會清除事務隊列,並放棄執行事務
以下是一個事務例子, 它原子地增加了 foo
和 bar
兩個鍵的值:
> MULTI
OK
> INCR foo
QUEUED
> INCR bar
QUEUED
> EXEC
1) (integer) 1
2) (integer) 1
EXEC 命令的回覆是一個數組, 數組中的每個元素都是執行事務中的命令所產生的回覆。 其中, 回覆元素的先後順序和命令發送的先後順序一致。
當客戶端處於事務狀態時, 所有傳入的命令都會返回一個內容爲 QUEUED
的狀態回覆(status reply), 這些被入隊的命令將在 EXEC命令被調用時執行。
事務中的錯誤
使用事務可能會用到下面2種錯誤
1.事務在執行exec之前,入列的命令可能會有錯,如果說,命令語法錯誤
2.命令在調用exec調用之後失敗。比如事務命令可能處理了錯誤類型的鍵,把列表命令的鍵放到string中
對於發生在exec執行之前的錯誤,客戶端的做法是檢查命令入列得到的值,如果命令是queue,則入列成功,否則,入列失敗,如果入列失敗,則大部分事務都會停止並取消這個事務
對於發生在exec執行之後的錯誤,並沒有對他們進行特別處理:即使事務中某個/某些事務發生錯誤,而其它的命令仍然會繼續執行
爲什麼Redis不支持回滾
如果你有使用關係式數據的經驗,那麼“redis在事務失敗時不進行回滾,而是繼續執行餘下的命令”這種做法可能會讓你覺得有點奇怪
以下是這種做法的優點:
1.Redis命令只會因爲錯誤的語法而失敗(並且這些問題不能在入隊時發現),或是命令用在了錯誤類型的鍵上面:也就是說,從實用性的角度來說,失敗的命令是由編程錯誤造成的,而這些錯誤應該在開發的過程中被發現,而不應該出現在生產環境中
2.因爲不需要對回滾進行支持,所以Redis的內部保存簡單且快速
放棄事務
當執行discard命令時,事務會被放棄,並且客戶端會從事務狀態中退出
使用check-and-set操作實現樂觀鎖
watch命令可以爲redis事務提供check-and-set(cas)行爲
被watch的鍵會被監視,並會發覺這些鍵是否被改動。如果至少一個被監視的鍵在exec執行之前被修改了,那麼整個事務都會被取消,exec返回空多條批量回復來表示事務已經失效
舉個例子,假設客戶端有A和B 2個都讀取了鍵原來的值,比如10,執行incr操作的時候,那麼2個客戶端都會將鍵的值設爲11,但正確返回結果應該是12纔對
有了watch,我們就可以輕鬆地解決這類問題了:
watch mykey
val=get mykey
val=val+1
multi
set mykey $val
exec
使用上面的代碼,如果有了watch之後,exec執行之前,有其他客戶端修改了mykey的值,那麼當前客戶端的事務就會失效。程序需要做的,就是不斷重試這個操作,知道沒有發生碰撞爲止
這種形式的鎖被稱爲樂觀鎖,它是非常強大的鎖機制。並且因爲大多數情況下,不同的客戶端會訪問不同的值,碰撞的情況一般都很少,所以通常並不需要重試