Redis - 事務與樂觀鎖

事務

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章