Redis-事務即簡單鎖應用


Redis支持簡單的事務, Redis允許一組命令在單一步驟中執行, 事務有兩個屬性

  • 事務是一個單獨的隔離操作, 事務中所有的命令都會序列化, 按照順序執行.
  • Redis事務是原子性的, 即要么都執行, 要么都不執行

一個事務從開始到執行會經歷三個階段

  • 開始事務
  • 命令入隊
  • 執行事務

redis 與 mysql 事務的對比:

mysql redis
開啟 start transaction multi
語句 普通的sql 普通命令
失敗 rollback回滾 discard取消
成功 commit exec
__注__: rollback 與 discard 的區別 如果已經成功執行了2條語句, 第3條語句出錯 rollback后, 前兩條語句影響消失 discard只是結束本次事務, 前兩條語句造成的影響依然存在 __注__: 在multi后面的語句中, 語句出錯可能有2種情況 1. 語法本身有問題, 這種錯誤exec時報錯, 所有語句得不到執行 2. 語法本身沒錯誤, 但適用對象有問題, 比如zadd操作list對象, exec后會執行正確的語句, 並跳過有不適當的語句

使用redis模擬銀行轉賬操作:

  • 正常情況
127.0.0.1:6379> set wang 200    #wang有200
OK
127.0.0.1:6379> set zhao 700    #zhao有700
OK
127.0.0.1:6379> 
127.0.0.1:6379> multi    #開啟事務
OK
127.0.0.1:6379> decrby zhao 100    #zhao減100
QUEUED
127.0.0.1:6379> incrby wang 100    #wang加100
QUEUED
#以上兩個QUEUED表示兩條語句被放在隊列里面, exec前並沒有執行
127.0.0.1:6379> exec    #執行完畢
1) (integer) 600
2) (integer) 300
127.0.0.1:6379> 
  • 意外情況
127.0.0.1:6379> multi    開啟事務
OK
127.0.0.1:6379> 
127.0.0.1:6379> decrby zhao 100    #zhao減100
QUEUED
127.0.0.1:6379> das    #輸入一個錯誤的命令
(error) ERR unknown command 'das'
127.0.0.1:6379> exec    #執行, 提示被忽略
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> 
127.0.0.1:6379> mget zhao wang    #zhao 和 wang的值不變
1) "600"
2) "300"
127.0.0.1:6379> 
  • 另一種報錯情況
127.0.0.1:6379> multi
OK
127.0.0.1:6379> 
127.0.0.1:6379> decrby zhao 100    #zhao減100
QUEUED
127.0.0.1:6379> sadd wang pig    #故意把wang當做數組加入一個key, 發現並沒有報錯, 
#因為這條語句被存放在隊列里, 並沒有被執行
QUEUED
127.0.0.1:6379> exec    #此時語句才被執行, 所以報錯, 但是zhao依然減了100, 說明執行了正確的語句, 跳過了不正取的語句, 影響還在
1) (integer) 500
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> 
127.0.0.1:6379> mget zhao wang 
1) "500"
2) "300"
127.0.0.1:6379> 
  • discard取消
127.0.0.1:6379> mget wang zhao
1) "400"
2) "400"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby zhao 100
QUEUED
127.0.0.1:6379> incrby wang 100
QUEUED
127.0.0.1:6379> 
127.0.0.1:6379> discard    #取消后值沒變
OK
127.0.0.1:6379> 
127.0.0.1:6379> mget wang zhao
1) "400"
2) "400"
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI
127.0.0.1:6379> 
127.0.0.1:6379> mget zhao wang
1) "400"
2) "400"
127.0.0.1:6379> 
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby zhao 100
QUEUED
127.0.0.1:6379> sadd wang pig
QUEUED
127.0.0.1:6379> 
127.0.0.1:6379> exec
1) (integer) 300
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> 
127.0.0.1:6379> discard    #不能discard
(error) ERR DISCARD without MULTI
127.0.0.1:6379> 
127.0.0.1:6379> mget zhao wang
1) "300"
2) "400"
127.0.0.1:6379> 



watch key1 key2 ... keyN

作用: 監聽 key1 key2 keyN有沒有變化, 如果有變化, 則事務取消

unwatch(不加key): 取消所有 watch 監聽

場景: 一個人正在買票, ticket-1, money-100, 而票只有一張, 如果在我multi之后, exec之前票被別人買走, 即ticket變為0了, 怎么辦?

127.0.0.1:6379> set ticket 1    #加入只有1張票
OK
127.0.0.1:6379> set lisi 300    #lisi有300
OK
127.0.0.1:6379> set wang 300    #wang有300
OK
127.0.0.1:6379> 
127.0.0.1:6379> multi    #開啟事務
OK
127.0.0.1:6379> decr ticket    #票數-1
QUEUED
127.0.0.1:6379> decrby lisi 100    #lisi准備買-100
QUEUED
127.0.0.1:6379> #此處還沒有exec

假如就在exec前票被別人買走, 打開另一個終端

127.0.0.1:6379> decr ticket    #票-1
(integer) 0
127.0.0.1:6379> get ticket    #此時票數為0
"0"
127.0.0.1:6379> 

此時提交lisi

127.0.0.1:6379> exec
1) (integer) -1    #票變為-1
2) (integer) 200    #錢-100
127.0.0.1:6379> 

因此上面的過程不合理


要解決上面的情況,要采用監視

127.0.0.1:6379> set ticket 1    #票數為1
OK
127.0.0.1:6379> set lisi 200    #lisi錢是100
OK
127.0.0.1:6379> set wang 300    #wang是300
OK
127.0.0.1:6379> 
127.0.0.1:6379> watch ticket    #監控ticket有沒有變動, 有變動的話則事務取消
OK
127.0.0.1:6379> multi    #開啟事務
OK
127.0.0.1:6379> decr ticket    #ticket-1
QUEUED
127.0.0.1:6379> decrby lisi 100    #錢-100
QUEUED
127.0.0.1:6379>

在exec前票又被另一個人買走了

127.0.0.1:6379> decr ticket
(integer) 0
127.0.0.1:6379> get ticket
"0"
127.0.0.1:6379> 

此時票數為0, lisi提交

127.0.0.1:6379> 
127.0.0.1:6379> exec    #失敗
(nil)
127.0.0.1:6379> 
127.0.0.1:6379> get lisi    #錢並沒有減少
"200"
127.0.0.1:6379> 



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM