Redis之坑:理解Redis事務

相關命令

命令 格式 做用 返回結果
WATCH WATCH key [key ...] 將給出的Keys標記爲監測態,做爲事務執行的條件 always OK.
UNWATCH UNWATCH 清除事務中Keys監測態,若是調用了EXEC or DISCARD,則沒有必要再手動調用UNWATCH always OK.
MULTI MULTI 顯式開啓redis事務,後續commands將排隊,等候使用EXEC進行原子執行 always OK.
EXEC EXEC 執行事務中的commands隊列,恢復鏈接狀態。若是WATCH在以前被調用,只有監測中的Keys沒有被修改,命令纔會被執行,不然中止執行(詳見下文,CAS機制 成功: 返回數組 —— 每一個元素對應着原子事務中一個 command的返回結果;
失敗: 返回NULLRuby 返回`nil`);
DISCARD DISCARD 清除事務中的commands隊列,恢復鏈接狀態。若是WATCH在以前被調用,釋放 監測中的Keys always OK.

注意:程序員

------MULTI,EXEC,DISCARD纔是顯式開啓並控制事務的經常使用命令,可類比關係型數據庫中的 BEGAIN,COMMIT,ROLLBACK(事實上,差距很大);redis

------WATCH命令的使用是爲了解決 事務併發 產生的不可重複讀幻讀的問題(簡單理解爲給Key加鎖);數據庫


Redis事務

MULTI, EXEC, DISCARD and WATCH 是Redis事務的基礎。用來顯式開啓並控制一個事務,它們容許在一個步驟中執行一組命令。並提供兩個重要的保證:編程

  • 事務中的全部命令都會被序列化並按順序執行。在執行Redis事務的過程當中,不會出現由另外一個客戶端發出的請求。這保證 命令隊列 做爲一個單獨的原子操做被執行。
  • 隊列中的命令要麼所有被處理,要麼所有被忽略。EXEC命令觸發事務中全部命令的執行,所以,當客戶端在事務上下文中失去與服務器的鏈接,
  • 若是發生在調用MULTI命令以前,則不執行任何commands
  • 若是在此以前EXEC命令被調用,則全部的commands都被執行。

同時,redis使用AOF(append-only file),使用一個額外的write操做將事務寫入磁盤。若是發生宕機,進程奔潰等狀況,可使用redis-check-aof tool 修復append-only file,使服務正常啓動,並恢復部分操做。數組


用法

使用MULTI命令顯式開啓Redis事務。 該命令老是以OK迴應。此時用戶能夠發出多個命令,Redis不會執行這些命令,而是將它們排隊EXEC被調用後,全部的命令都會被執行。而調用DISCARD能夠清除事務中的commands隊列退出事務bash

  • 如下示例以原子方式,遞增鍵foo和bar。
>MULTI
OK
>INCR foo
QUEUED
>INCR bar
QUEUED
>EXEC
1)(整數)1
2)(整數)1
複製代碼

從上面的命令執行中能夠看出,EXEC返回一個數組其中每一個元素都是事務中單個命令的返回結果,並且順序與命令的發出順序相同。 當Redis鏈接處於MULTI請求的上下文中時,全部命令將以字符串QUEUED從Redis協議的角度做爲狀態回覆發送)做爲回覆,並在命令隊列中排隊。只有EXEC被調用時,排隊的命令纔會被執行,此時纔會有真正的返回結果服務器


事務中的錯誤

事務期間,可能會遇到兩種命令錯誤:併發

  • 在調用EXEC命令以前出現錯誤(COMMAND排隊失敗)。
  • 例如,命令可能存在語法錯誤(參數數量錯誤,錯誤的命令名稱...);
  • 或者可能存在某些關鍵條件,如內存不足的狀況(若是服務器使用maxmemory指令作了內存限制)。

客戶端會在EXEC調用以前檢測第一種錯誤。 經過檢查排隊命令的狀態回覆(***注意:這裏是指排隊狀態回覆,而不是執行結果***),若是命令使用QUEUED進行響應,則它已正確排隊,不然Redis將返回錯誤。若是排隊命令時發生錯誤,大多數客戶端將停止該事務並清除命令隊列。然而:app

  • Redis 2.6.5以前,這種狀況下,在EXEC命令調用後,客戶端會執行命令的子集(成功排隊的命令)而忽略以前的錯誤。
  • Redis 2.6.5開始,服務端會記住在累積命令期間發生的錯誤,當EXEC命令調用時,將拒絕執行事務,並返回這些錯誤,同時自動清除命令隊列
  • 示例以下:
>MULTI
+OK
>INCR a b c
-ERR wrong number of arguments for 'incr' command
複製代碼

這是因爲INCR命令的語法錯誤,將在調用EXEC以前被檢測出來,並終止事務(version2.6.5+)。ui

  • 在調用EXEC命令以後出現錯誤。
  • 例如,使用錯誤的值對某個key執行操做(如針對String值調用List操做)

EXEC命令執行以後發生的錯誤並不會被特殊對待即便事務中的某些命令執行失敗,其餘命令仍會被正常執行

  • 示例以下:
>MULTI
+OK
>SET a 3
+QUEUED
>LPOP a
+QUEUED
>EXEC
*2
+OK
-ERR Operation against a key holding the wrong kind of value
複製代碼
  • EXEC返回一個包含兩個元素的字符串數組,一個元素是OK,另外一個是-ERR……
  • 可否將錯誤合理的反饋給用戶這取決於客戶端library(如:Spring-data-redis.redisTemplate)的自身實現。
  • 須要注意的是,即便命令失敗,隊列中的全部其餘命令也會被處理----Redis不會中止命令的處理

Redis事務不支持Rollback(重點

事實上Redis命令在事務執行時可能會失敗,但仍會繼續執行剩餘命令而不是Rollback(事務回滾)。若是你使用過關係數據庫,這種狀況可能會讓你感到很奇怪。然而針對這種狀況具有很好的解釋:

  • Redis命令可能會執行失敗,僅僅是因爲錯誤的語法被調用(命令排隊時檢測不出來的錯誤),或者使用錯誤的數據類型操做某個Key: 這意味着,實際上失敗的命令都是編程錯誤形成的,都是開發中可以被檢測出來的,生產環境中不該該存在。(這番話,完全甩鍋,「都是大家本身編程錯誤,與咱們無關」。)
  • 因爲沒必要支持Rollback,Redis內部簡潔而且更加高效。

若是錯誤就是發生了呢?」這是一個反對Redis觀點的爭論。然而應該指出的是,一般狀況下,回滾並不能挽救編程錯誤。鑑於沒有人可以挽救程序員的錯誤,而且Redis命令失敗所需的錯誤類型不太可能進入生產環境,因此咱們選擇了不支持錯誤回滾(Rollback)這種更簡單快捷的方法。


清除命令隊列

DISCARD被用來停止事務。事務中的全部命令將不會被執行,鏈接將恢復正常狀態。

> SET foo 1
OK
> MULTI
OK
> INCR foo
QUEUED
> DISCARD
OK
> GET foo
"1"
複製代碼
相關文章
相關標籤/搜索