事務
Redis的事務與MySQL的事務差別較大。
Redis的事務本質是一組命令的集合
。也就是說一個事務中的所有命令都會被序列化,在事務執行的時候,會按照順序執行這些命令。
即Redis具有:一次性、順序性、排他性(不受其他命令干擾)。
在Redis中,事務沒有隔離級別的概念
。
Redis事務流程
1、開啓事務(multi)
2、命令入隊(即依次輸入命令)
3、執行事務(exec)
所有命令都是在事務中,並沒有直接執行,而是發起exec
執行命令的時候纔會依次執行命令。
Redis單條命令保證原子性,但是事務不保證原子性
一、執行事務
127.0.0.1:6379> multi # 開啓事務
OK
127.0.0.1:6379> set key1 value1
QUEUED
127.0.0.1:6379> set key2 value2
QUEUED
127.0.0.1:6379> get key1
QUEUED
127.0.0.1:6379> set key3 value3
QUEUED
127.0.0.1:6379> exec # 執行事務
1) OK
2) OK
3) "value1"
4) OK
從上面可以看出,命令按照順序來執行的,事務完畢。
二、放棄事務
如果我們輸入完命令,突然不想去執行,可以通過DISCARD
命令來放棄事務。
127.0.0.1:6379> multi # 開啓事務
OK
127.0.0.1:6379> set key1 value1
QUEUED
127.0.0.1:6379> set key2 value2
QUEUED
127.0.0.1:6379> get key1
QUEUED
127.0.0.1:6379> DISCARD # 放棄事務
OK
127.0.0.1:6379> get key1 # 測試放棄事務是否成功
(nil)
三、編譯型異常 - 所有事務命令不執行
當事務中某一條命令出現錯誤時,事務中所有的命令都不會執行。
127.0.0.1:6379> multi # 開啓事務
OK
127.0.0.1:6379> set key1 value1
QUEUED
127.0.0.1:6379> set key2 value2
QUEUED
127.0.0.1:6379> setget key2 # 命令錯誤
(error) ERR unknown command 'setget'
127.0.0.1:6379> get key1
QUEUED
127.0.0.1:6379> exec # 執行事務,報錯
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get key1 # 測試其他命令是否成功
(nil)
四、運行時異常 - 不影響其他命令
如果事務隊列中存在語法性錯誤,執行事務的時候,其他命令不會受到這個錯誤的影響,他們會正常執行,錯誤的命令會拋出異常。
127.0.0.1:6379> set key1 "value1"
OK
127.0.0.1:6379> get key1
"value1"
127.0.0.1:6379> multi # 開啓事務
OK
127.0.0.1:6379> set key2 value2
QUEUED
127.0.0.1:6379> incr key1 # 自增,因爲類型不符合會失敗
QUEUED
127.0.0.1:6379> get key2
QUEUED
127.0.0.1:6379> set key3 value3
QUEUED
127.0.0.1:6379> exec # 執行事務
1) OK
2) (error) ERR value is not an integer or out of range # 就這一條失敗
3) "value2"
4) OK
鎖 - 監控
悲觀鎖:很悲觀,認爲什麼時候都會出問題,無論做什麼都會加鎖!
樂觀鎖:很樂觀,認爲什麼時候都不會出問題,所以不會上鎖! 更新數據的時候去判斷一下,在此期間是否 有人修改過這個數據。
Redis實現樂觀鎖
在MySQL中通過對比version,在Redis中則是通過watch
監控來實現樂觀鎖。
通過兩個Redis客戶端模擬多線程下的操作。
1、Redis客戶端一
127.0.0.1:6379> set money 100 # 設置金額100
OK
127.0.0.1:6379> set out 0 # 設置消費0
OK
127.0.0.1:6379> watch money # 監控money
OK
127.0.0.1:6379> multi # 開啓事務
OK
127.0.0.1:6379> DECRBY money 20 # 金額-20
QUEUED
127.0.0.1:6379> INCRBY out 20 # 消費+20
QUEUED
127.0.0.1:6379> # 先不執行事務
Redis客戶端二
客戶端一的事務並未執行,此時客戶端二去修改了money的值。
127.0.0.1:6379> get money
"100"
127.0.0.1:6379> set money 1000 # 將金額設置爲1000
OK
127.0.0.1:6379> get money
"1000"
127.0.0.1:6379>
Redis客戶端一執行事務
此時,執行Redis客戶端一的事務。
127.0.0.1:6379> exec # 執行事務
(nil) # 執行失敗,因爲被監控的money已經被修改
127.0.0.1:6379>
Redis客戶端一解決問題
要解決這個問題,先要取消監控(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> DECRBY money 20 # 金額-20
QUEUED
127.0.0.1:6379> INCRBY out 20 # 消費+20
QUEUED
127.0.0.1:6379> exec # 執行事務
1) (integer) 980
2) (integer) 20