1. 程式人生 > >Redis 6. redis的事務

Redis 6. redis的事務

Redis

@Author:hanguixian
@Email:[email protected]

六 事務

1 是什麼

2 能幹嘛

  • 一個佇列中,一次性、順序性、排他性的執行一系列命令

3 使用

3.1 常用命令

  • DISCARD:取消事務,放棄執行事務塊內的所有命令
  • EXEC:執行所有事務塊內的命令
  • MULTI:標記一個事務塊的開始
  • UNWATCH:取消WATCH命令對所有key的監視
  • WATCH:監視一個(多個)key,如果事務執行之前這個(或這些)key被其他命令索改動,那麼事務將被打斷

3.2 執行的五種情況

3.2.1 正常執行
127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> KEYS *
(empty list or set)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379>
set k1 v1 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> get k1 QUEUED 127.0.0.1:6379> exec 1) OK 2) OK 3) "v1" 127.0.0.1:6379> mget k1 k2 1) "v1" 2) "v2"
3.2.2 放棄執行
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v11
QUEUED
127.0.0.1:6379> set k2 v22
QUEUED
127.0.0.1:6379> set
k3 v3 QUEUED 127.0.0.1:6379> get k2 QUEUED 127.0.0.1:6379> DISCARD OK 127.0.0.1:6379> get k2 "v2" 127.0.0.1:6379> exec (error) ERR EXEC without MULTI
3.2.3 全部失敗
127.0.0.1:6379> keys *
1) "k1"
2) "k2"
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> getset k3
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> KEYS *
1) "k1"
2) "k2"
3.2.4 部分失敗
127.0.0.1:6379> KEYS *
1) "k1"
2) "k2"
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k3 v33
QUEUED
127.0.0.1:6379> INCR k3
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) (error) ERR value is not an integer or out of range
3) "v2"
4) "v33"
5) OK
127.0.0.1:6379> KEYS *
1) "k4"
2) "k1"
3) "k3"
4) "k2"
3.2.5 watch監控
  • 悲觀鎖/樂觀鎖/CAS(Check And Set)
    • 悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,這樣別人想拿這個資料就會block直到它拿到鎖。傳統的關係型資料庫裡邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖
    • 樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿資料的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個資料,可以使用版本號等機制。樂觀鎖適用於多讀的應用型別,這樣可以提高吞吐量,
      • 樂觀鎖策略:提交版本必須大於記錄當前版本才能執行更新
  • 操作
    • 初始化信用卡可用餘額和欠額
    • 無加塞篡改,先監控再開啟multi,保證兩筆金額變動在同一個事務內
    • 有加塞篡改
      • 監控了key,如果key被修改了,後面一個事務的執行失效
    • unwatch
    • 一旦執行了exec之前加的監控鎖都會被取消掉了
  • 小結
    • Watch指令,類似樂觀鎖,事務提交時,如果Key的值已被別的客戶端改變,比如某個list已被別的客戶端push/pop過了,整個事務佇列都不會被執行
    • 通過WATCH命令在事務執行之前監控了多個Keys,倘若在WATCH之後有任何Key的值發生了變化,EXEC命令執行的事務都將被放棄,同時返回Nullmulti-bulk應答以通知呼叫者事務執行失敗
127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> KEYS *
(empty list or set)
127.0.0.1:6379> set balance 100
OK
127.0.0.1:6379> set dept 0
OK
127.0.0.1:6379> mget balance dept
1) "100"
2) "0"
127.0.0.1:6379> WATCH balance
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY balance 20
QUEUED
127.0.0.1:6379> INCRBY dept 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
127.0.0.1:6379> mget balance dept
1) "80"
2) "20"
127.0.0.1:6379> WATCH balance
OK
######################在另外一個終端執行########################
[[email protected] bin]# redis-cli -p 6379
127.0.0.1:6379> get balance
"80"
127.0.0.1:6379> set balance 800
OK
#######################結束,切回原來的終端執行#################
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY balance 20
QUEUED
127.0.0.1:6379> INCRBY dept 20
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> mget balance dept
1) "800"
2) "20"
127.0.0.1:6379> WATCH balance 
OK
127.0.0.1:6379> set balance 500
OK
#####在另一個終端修改balance####
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set balance 100
QUEUED
127.0.0.1:6379> get balance
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> UNWATCH
OK
127.0.0.1:6379> WATCH balance 
OK
127.0.0.1:6379> set balance 100
OK
127.0.0.1:6379> get balance
"100"
127.0.0.1:6379> WATCH balance 
OK
#####在另一個終端修改balance####
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set balance 100
QUEUED
127.0.0.1:6379> exec
(nil)
##一旦執行了exec之前加的監控鎖都會被取消掉了##
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set balance 100
QUEUED
127.0.0.1:6379> exec
1) OK
127.0.0.1:6379> get balance
"100"

3.3 總結

3.3.1 三階段
  • 開啟:以MULTI開始一個事務
  • 入隊:將多個命令入隊到事務中,接到這些命令並不會立即執行,而是放到等待執行的事務佇列裡面
  • 執行:由EXEC命令觸發事務
3.3.2 三特性
  • 執行:由EXEC命令觸發事務
  • 單獨的隔離操作:事務中的所有命令都會序列化、按順序地執行。事務在執行的過程中,不會被其他客戶端傳送來的命令請求所打斷。
  • 沒有隔離級別的概念:佇列中的命令沒有提交之前都不會實際的被執行,因為事務提交前任何指令都不會被實際執行,也就不存在”事務內的查詢要看到事務裡的更新,在事務外查詢不能看到”這個讓人萬分頭痛的問題
  • 不保證原子性:redis同一個事務中如果有一條命令執行失敗,其後的命令仍然會被執行,沒有回滾