一、Redis事務的本質
一組命令的集合,一個事務中的所有命令都會被序列化,在事務執(zhí)行過程中,會按照順序執(zhí)行。
------頭部 -> set -> set -> set -> 尾部------
特性:
- 一次性
- 順序性
- 排他性
Redis事務沒有隔離級別的概念。
Redis單條指令是保證原子性的,但是事務不保證原子性。
二、Redis事務實踐
主要命令如下:
- MULTI ->開啟事務
- EXEC ->執(zhí)行事務
- DISCARD ->放棄事務
執(zhí)行效果如下所示
1.正常執(zhí)行事務
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> get k2
QUEUED
127.0.0.1:6379> EXEC #執(zhí)行事務
1) OK
2) OK
3) "v1"
4) "v2"
127.0.0.1:6379>
2.放棄當前事務
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> DISCARD #放棄事務
OK
127.0.0.1:6379> get k1 #事務已經(jīng)被放棄,不會取到值
(nil)
127.0.0.1:6379> get k2
(nil)
3.編譯時發(fā)生錯誤
當事務開啟之后,多條命令中包含一條不存在的命令,本次事務會全部放棄。
127.0.0.1:6379> MULTI #開啟事務
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> setget k2 v2 #此處故意輸入錯誤命令,編譯時已經(jīng)報錯
(error) ERR unknown command `setget`, with args beginning with: `k2`, `v2`,
127.0.0.1:6379> EXEC #執(zhí)行事務,事務全部放棄,不會成功
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k1 #沒有取到編譯錯誤之前設置的值
(nil)
4.執(zhí)行時發(fā)生錯誤
當事務開啟之后,某些命令執(zhí)行了不合理的操作,那么該命令不會生效,但不影響本次事務中其他命令的執(zhí)行。
127.0.0.1:6379> set k1 wuhan
OK
127.0.0.1:6379> get k1
"wuhan"
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k2 shanghai
QUEUED
127.0.0.1:6379> INCR k1
QUEUED
127.0.0.1:6379> set k3 beijing
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) (error) ERR value is not an integer or out of range #此處k1不是數(shù)字,執(zhí)行incr會報錯
3) OK
127.0.0.1:6379> get k1
"wuhan"
127.0.0.1:6379> get k2 #雖然執(zhí)行命令INCR k1時出錯,沒有影響其余正常命令的執(zhí)行
"shanghai"
127.0.0.1:6379> get k3
"beijing"
三、Redis實現(xiàn)樂觀鎖
樂觀鎖
- 做事很樂觀,認為什么時候都不會出問題,所以不會真正上鎖。在更新數(shù)據(jù)的時候去判斷一下,在此期間內(nèi)是否有人修改過數(shù)據(jù)。
- 獲取version
- 更新的時候比較version
Redis中如何實現(xiàn)?
WATCH key [key ...]
在操作之前監(jiān)視指定的key,如果事務執(zhí)行期間被別的線程修改過,那么整個事務將會失效。
線程1:
127.0.0.1:6379> set money 1000 #假設賬戶有1000元
OK
127.0.0.1:6379> WATCH money #取之前先監(jiān)視key
OK
127.0.0.1:6379> MULTI #開啟事務
OK
127.0.0.1:6379(TX)> DECRBY money 500 #取500
QUEUED
127.0.0.1:6379(TX)> EXEC #執(zhí)行事務,返回結果為(nil),代表事務執(zhí)行失敗
(nil)
127.0.0.1:6379> get money #查看金額已經(jīng)發(fā)生變動
"600"
線程2:
127.0.0.1:6379> get money #賬戶起始金額為1000
"1000"
127.0.0.1:6379> DECRBY money 400 #取400,余額為600
(integer) 600