【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 實現樂觀鎖的操作,一般使用場景會放到秒殺系統裡面進行應用
參考資料:
歡迎點贊,關注,收藏
朋友們,你的支援和鼓勵,是我堅持分享,提高質量的動力
好了,本次就到這裡
技術是開放的,我們的心態,更應是開放的。擁抱變化,向陽而生,努力向前行。
我是小魔童哪吒,歡迎點贊關注收藏,下次見~