Redis事務提供2個重要保證
MULTI, EXEC, DISCARD 和WATCH命令是Redis事務操作的基礎 。他們可以讓Redis在一個步驟裡執行一組命令,且能做到如下2個重要保證:
事務中的所有命令都是序列化且都是按順序執行的。在一個客戶端執行Redis事務的過程中,不會接收其他任何客戶端對它發出的請求。這保證了這些命令是作為一個單獨的獨立操作執行的。所有的命令要麼都被一起處理,要麼全都沒有被處理,所以Redis事務是原子的。EXEC會命令觸發事務中所有命令的執行。當正在使用AOF時,Redis會使用一個簡單的write(2)系統級調用來確保把事務寫入到磁碟。但是,如果Redis服務崩潰,或者被系統管理員以某種強制的方式殺死,那麼可能只有部分命令被寫入到磁碟。Redis在重新啟動時會檢測這種情況,並報錯,然後退出。使用 redis-check-aof工具可以檢查AOF,並移除那不完整的事務,使服務可以再次啟動
從 2.2 開始, Redis 可以用另外一個形式(樂觀鎖)來確保以上2點,後面講詳細討論.
用法
使用 MULTI 命令進入事務模式.這個命令只會返回OK。這個時候,用戶就可以發出多個要一起執行的命令了。 Redis暫時不會執行這些命令,而是把它們放進隊列。 當 EXEC 被調用時,所有的命令才會被一次性執行.
相反的,如果調用了 DISCARD 命令,則會清除事務隊列中的所有命令,然後退出事務.
下面一個示例,原子性的遞增keys foo 和 bar。
從上面可以看出, EXEC 返回一個數組, 包含事務中每個命令的執行結果,且響應的順序和命令發出時的順序一樣。
當一個Redis連結正處於一個MULTI 請求的上下文時, 所有命令的響應結果都是字符串QUEUED。當調用EXEC時,會一次性執行這些命令。
錯誤
在Redis事物中,可能會發生2種錯誤:
命令可能排隊失敗。如,命令的語法可能是錯誤 (錯誤的參數個數,錯誤的命令名字 ...),或一些重要的環境問題,如,內存不足。當EXEC 調用後 ,一些命令可能執行失敗,如,在一個字符串上進行了列表命令的操作。
第一種錯誤發生在 EXEC 命令之前,是很容易發現的,可以通過檢查命令的返回值:如果返回值是 QUEUED ,那麼它就是成功, 不然就是失敗,Redis會返回一個錯誤。如果在命令的隊列期間發出一個錯誤,通常的做法是,終止事務。
從 Redis 2.6.5 開始, 在命令排隊期間發生錯誤,Redis會拒絕執行 EXEC,並返回一個錯誤,然後自動放棄這個事務。
在 Redis 2.6.5 之前,EXEC調用後,會執行排隊成功的命令,忽略失敗的命令。
這新的規則讓事務和管道的一起使用變得簡單, 這樣整個事務都可以一起發送,然後一次性獲取所有的回覆,可以節省來往交互操作。
執行 EXEC 後:所有的命令都會被執行,甚至是那些錯誤的命令。
在下面的例子中,事務被執行,其中有一個命令返回失敗:
EXEC 返回2個字符串 ,其中一個是OK ,另一個是-ERR, 由客戶端來決定如何處理這個結果。
要注意的是 就算有命令失敗,隊列中的其他命令也會被執行。
當發生語法錯誤的時候,會儘快的報告錯誤:
以上是 INCR 命令的語法錯誤,它不會被放進命令隊列。
為什麼Redis不支持回滾
Redis命令在事務中可能會執行失敗,但是Redis事務不會回滾,而是繼續會執行餘下的命令。如果您有一個關係型資料庫的知識,這對您來說可能會感到奇怪,因為關係型數據在這種情況下都是會回滾的。
Redis這樣做,主要是因為:
只有當發生語法錯誤(這個問題在命令隊列時無法檢測到)了,Redis命令才會執行失敗, 或對keys賦予了一個類型錯誤的數據:這意味著這些都是程序性錯誤,這類錯誤在開發的過程中就能夠發現並解決掉,幾乎不會出現在生產環境。由於不需要回滾,這使得Redis內部更加簡單,而且運行速度更快。
放棄事務
DISCARD可以中止一個事務。這種情況下,不會執行任何命令,然後客戶端的連結的狀態會還原成正常狀態。
樂觀鎖(check-and-set)
WATCH 命令為事務提供一個check-and-set (CAS) 行為。
WATCH 可以用來監聽事務中的隊列中的命令,在EXEC之前,一旦發現有一個命令被修改了的 , 那麼整個事務就會終止, EXEC返回一個 Null ,提示用戶事務失敗了。
例如, 我們要自動的遞增一個key (假設Redis還沒有實現 INCR命令).
那代碼可能如下:
如果在同一時間,只有一個客戶端在執行上面的操作,那麼上面的操作是可靠的。但如果同時有多個用戶執行上面的操作的話,那結果就是不可靠的了
我們可以使用 WATCH 來很好的解決這個問題:
使用以上的代碼,當有另一個用戶在我們調用WATCH 和 EXEC之間,修改了mykey的值val,那麼這個事務就會失敗。這種形式的鎖稱為樂觀鎖,是一種非常強大的鎖。
WATCH 說明
WATCH 到底是什麼意思呢? 這個命令使得 EXEC 命令的執行必須滿足一個條件:如果被WATCH的 keys 沒有一個被更改(但它們可以在事務中被修改),則執行事務;不然,就不會執行這個事務。(注意,如果你 WATCH了一個有生命周期的key,並且這個key過期了, EXEC 依然會執行)
WATCH 可以被多次調用。所有的WATCH 調用都會在 EXEC 調用之前起作用。WATCH可以接收任意多的key 。
當 EXEC 被調用後, 所有的keys都將UNWATCH,不管這個事務會不會終止。同樣,當一個客戶端連結關閉後, 一切都將UNWATCH。
可以使用UNWATCH (沒有參數)命令來刷新所有被WATCH的keys。有時會這樣操作,我們樂觀地鎖定了幾個keys,因為可能我們需要執行一個事務來修改這些keys,但是在讀取了keys的當前內容之後,我們不想繼續處理了。那麼這個時候,我們就可以調用UNWATCH。
用 WATCH 實現ZPOP
這是一個不錯的示例,它闡述了如何使用WATCH 來產生新的原子操作,這個沒有被Redis支持的ZPOP,這命令以原子的方式從一個有序集合中彈出較低的分數的值:
如果 EXEC 失敗 (如,返回 Null ) 我們只需要重新執行這個操作即可。