【Redis 系列】redis 學習六,redis 事務處理和監控事務

小魔童哪吒發表於2022-02-27

【Redis 系列】redis 學習六,redis 事務處理和監控事務

寫在前面

我們學過的事務都是保證原子性的,但是 redis 的事務中執行多個指令,是不保證原子性的

redis 事務的本質

就是一組命令的集合,一個事務中所有的命令都會被序列化,在事務執行的過程,是按照順序執行命令的,他們擁有

  • 一次性
  • 順序性
  • 排他性

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

redis 事務中,命令是這樣執行的

命令放在事務中,並沒有馬上執行,而是發起執行命令的時候才會執行,通過 exec 觸發

redis 是單條指令保證原子性,但是事務不保證原子性

執行一個事務的流程是這個樣子的:

  • multi 開啟事務
  • 各種命令入隊
  • exec 執行事務

開啟事務

MULTI

開啟一個事務

EXEC

執行事務裡面的指令

執行事務完畢後,需要再使用事務,那麼需要再次開啟事務

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set name xiaozhu
QUEUED
127.0.0.1:6379(TX)> set age 19
QUEUED
127.0.0.1:6379(TX)> set hobby paly
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) OK
4) OK
5) OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set city changsha
QUEUED
127.0.0.1:6379(TX)> get city
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) "changsha"

放棄事務

DISCARD

放棄最近開啟的事務

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set name xiaozhu
QUEUED
127.0.0.1:6379(TX)> set age 10
QUEUED
127.0.0.1:6379(TX)> set city beijing
QUEUED
127.0.0.1:6379(TX)> DISCARD
OK
127.0.0.1:6379> get city
(nil)
127.0.0.1:6379> get name
(nil)

事務出錯

事務出錯分為兩種:

  • 編譯型出錯
  • 執行時出錯

編譯型出錯

程式碼編寫出錯,無法通過編譯,此時事務裡面編寫的指令,全部執行失敗

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set name xiaozhu
QUEUED
127.0.0.1:6379(TX)> set age 10
QUEUED
127.0.0.1:6379(TX)> set hobby play
QUEUED
127.0.0.1:6379(TX)> set city changsha
QUEUED
127.0.0.1:6379(TX)> set dream xxx
QUEUED
127.0.0.1:6379(TX)> setget hahaha  # 指令不存在,編譯不通過,直接報錯
(error) ERR unknown command `setget`, with args beginning with: `hahaha`,
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get city
(nil)

一旦編譯出錯,事務裡面的所有指令都不會執行

執行時出錯

程式執行時某一個指定的某個邏輯出現問題,僅僅影響本條執行的執行,並且本條指令會跑出異常,不影響事務中其他指令的執行

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set name xiaozhu
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> get name
QUEUED
127.0.0.1:6379(TX)> INCR name  # incr 指令用法正確,只不過 name 是字串,執行的時候,是無法執行通過的
QUEUED
127.0.0.1:6379(TX)> set age 29
QUEUED
127.0.0.1:6379(TX)> set hobby play
QUEUED
127.0.0.1:6379(TX)> exec
1) "xiaozhu"
2) (error) ERR value is not an integer or out of range
3) OK
4) OK
127.0.0.1:6379> get hobby
"play"
127.0.0.1:6379> get name
"xiaozhu"

根據上述結果,incr 指令的用法正確,但是 incr 只能對數字進行操作,name 是一個字串,無法做自增操作,因此執行時報錯,事務中不影響其他的指令執行

redis 的 watch 監控

watch 監控,可以說說 樂觀鎖 和 悲觀鎖

樂觀鎖

  • 很樂觀,認為什麼時候都不會出問題,所以不會上鎖,就更新資料的時候會去判斷一下在此期間若資料發生了變化
  • 樂觀鎖會先獲取一個基礎資料版本
  • 更新資料的時候,會比較這個版本是否發生變化,若發生變化,則更新失敗,若未發生變化,則更新資料

悲觀鎖

  • 很悲觀,無論什麼時候都認為會出問題,都要加鎖

redis 的監控測試

用 watch 來加鎖模擬銀行取錢

  • money 賬戶 有 2000
  • outer 賬戶 有 500
  • money 賬戶 給 outer 賬戶 500
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> set money 2000
OK
127.0.0.1:6379> set outer 500
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> DECRBY money 500
QUEUED
127.0.0.1:6379(TX)> INCRBY outer 500
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 1500
2) (integer) 1000

開始測試模擬多個執行緒來操作

redis 客戶端 1

  • money 賬戶 有 2000
  • outer 賬戶 有 500
  • money 賬戶 給 outer 賬戶 500 ,還未開始執行
127.0.0.1:6379> set money 2000
OK
127.0.0.1:6379> set outer 500
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> DECRBY money 500
QUEUED
127.0.0.1:6379(TX)> INCRBY outer 500
QUEUED

客戶端 1 的事務操作還未開始執行,客戶端 2 就進行了如下操作

127.0.0.1:6379> set money 10000
OK
127.0.0.1:6379> set money 10000
OK

此時執行客戶端 1 的 exec 指令

127.0.0.1:6379(TX)> exec
(nil)
127.0.0.1:6379> get money
"10000"

發現客戶端 1 執行 exec 的時候,是一個 nil ,說明變更失敗,是因為 watch 監控到 money 有變動,因此事務執行失敗

unwatch

發現事務執行失敗了,需要使用命令 unwatch 解除監控

127.0.0.1:6379> UNWATCH
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> DECRBY money 1000
QUEUED
127.0.0.1:6379(TX)> INCRBY outer 1000
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 9000
2) (integer) 1500

這就是 redis 實現樂觀鎖的操作,一般使用場景會放到秒殺系統裡面進行應用

參考資料:

redis_doc

歡迎點贊,關注,收藏

朋友們,你的支援和鼓勵,是我堅持分享,提高質量的動力

好了,本次就到這裡

技術是開放的,我們的心態,更應是開放的。擁抱變化,向陽而生,努力向前行。

我是小魔童哪吒,歡迎點贊關注收藏,下次見~

相關文章