1. 程式人生 > 實用技巧 >redis事物學習筆記

redis事物學習筆記

Redis事物概念

首先非常重要的一個概念: redis是單執行緒執行命令的,也就是說使用者所有的請求都會被序列化,redis同一時間內只會幫使用者執行一條命令,其餘的都需要進行等待。

Redis 事務的本質是一組命令的集合。

事務支援一次執行多個命令,一個事務中所有命令都會被序列化。

在事務執行過程,會按照順序序列化執行佇列中的命令,其他客戶端提交的命令請求不會插入到事務執行命令序列中。

總結說:redis事務就是一次性、順序性、排他性的執行一個佇列中的一系列命令。

lua指令碼是怎麼保證原子性的?

Redis使用同一個Lua直譯器來執行所有命令,同時,Redis保證以一種原子性的方式來執行指令碼:當lua指令碼在執行的時候,不會有其他指令碼和命令同時執行,這種語義類似於 MULTI/EXEC。從別的客戶端的視角來看,一個lua指令碼要麼不可見,要麼已經執行完。

然而這也意味著,執行一個較慢的lua指令碼是不建議的,由於指令碼的開銷非常低,構造一個快速執行的指令碼並非難事。但是你要注意到,當你正在執行一個比較慢的指令碼時,所以其他的客戶端都無法執行命令。

當然所有對redis操作時間過長的命令,都不建議使用,例如 keys * ,如果資料量過大的話,redis會一直在查詢,導致後續的命令無妨執行,從而客戶端可能也在不停的等待,造成系統Hang住。

如上圖,所有的命令都在排隊等待redis執行。

Redis事務沒有隔離級別的概念

批量操作在傳送 EXEC 命令前被放入佇列快取,並不會被實際執行,也就不存在事務內的查詢要看到事務裡的更新,事務外查詢不能看到。

Redis不保證原子性

Redis中,單條命令是原子性執行的,但事務不保證原子性,且沒有回滾。事務中任意命令執行失敗,其餘的命令仍會被執行。

也就是說,Redis的事物和傳統的關係型資料庫(例如MySQL)是不同的,MySQL可以保證一個事物中其中一步執行失敗,其餘的資料都回滾成修改前的狀態,但是Redis不可以,其中一個數據修改失敗了,其餘的依舊正常執行。

Redis事務的三個階段

  1. 開始事務
  2. 命令入隊
  3. 執行事務

Redis事務相關命令

  1. watch key1 key2 … : 監視一或多個key,如果在事務執行之前,被監視的key被其他命令改動,則事務被打斷 ( 類似樂觀鎖 )

  2. multi : 標記一個事務塊的開始( queued )

  3. exec : 執行所有事務塊的命令 ( 一旦執行exec後,之前加的監控鎖都會被取消掉 )

  4. discard : 取消事務,放棄事務塊中的所有命令

  5. unwatch : 取消watch對所有key的監控

Redis事物使用示例

正常執行

通過 multi 開啟一個事物 , 隨後我們按序存入了 k1,k2,k3 ,

並且最終get獲取k1的結果,

但是我們輸入這些命令後,

redis給我們返回的結果都是QUEUED,

意味著它並沒有立即執行,而是存入了一個佇列,

等到我們輸入exec這個命令後,redis才真正的執行了我們上面的命令,並且按照順序返回了結果。

取消事物

首先我們獲取k1的值,這時候是k1,

當我們開啟一個事物,將k1的值改為change後,

取消了事物,

最終再確認一下,發現k1的值沒有更改還是k1,說明事物被成功取消,沒有執行。

事物執行過程中,發生語法錯誤

首先獲取k1,值也是k1,

獲取k5的值是空 nil,不存在

隨後開啟一個事物,並設定k1為change,

然後設定k2時發生語法錯誤,

再設定k5,最終執行事物,

我們看到redis返回的結果是事物執行失敗,取消了事物,

這時候看k1和k5的值和執行事物之前的一樣,說明發生語法錯誤的話,整個事物的命令都將不會執行。

這種錯誤類似於編譯錯誤,如果java程式碼編譯失敗的話,後續將不會執行。

事物執行過程中,發生命令執行錯誤

首先獲取k1,值也是k1,

獲取k5的值是空 nil,不存在

隨後開啟一個事物,並將k1的值進行自增操作,

但是因為k1的值是字串型別,無法自增,這時候命令語法是沒有錯誤的,存入了隊列當中,

隨後我們設定k5的值為change,

redis這時候第一個命令發生執行時錯誤,自增失敗,但是第二個命令設定k5成功了,

這時候k1的值依然是k1,但是k5的值是change了,

這類似java的執行時異常,通過語法檢測後,redis開始執行命令,只要命令的語法沒有錯誤,redis不關心結果有沒有執行成功,依然會繼續去執行下一條命令,所以說redis的事物和mysql等關係型資料庫不一樣,mysql如果發生執行失敗,會進行回滾,redis不會。

使用watch監聽資料

首先我們設定一個k1 和 money的值,分別是k1 和 100,

隨後我們watch 監聽 這個money,然後開啟一個事物,

在事物中修改k1的值為666,將money自增1,變成101,

這時候我們不要執行exec事物,而是開啟另一個客戶端,執行修改money的命令,

隨後我們回到剛剛的事物命令客戶端,執行exec,

這時候我們可以看到,執行exec後,redis返回的結果是nil,

這說明當我們監聽一個key後,執行事物過程中,如果這個key的值被外界因素改動,當前事物則會被取消,所有的命令都將不會執行,只有當被監聽的key沒有被改變時,事物才可以成功。

如上,執行事物中,沒有修改監聽的key,則事物執行成功了。

watch指令類似於樂觀鎖,在事務提交時,如果watch監控的多個KEY中任何KEY的值已經被其他客戶端更改,則使用EXEC執行事務時,事務佇列將不會被執行,同時返回Nullmulti-bulk應答以通知呼叫者事務執行失敗。