雞肋的Redis事務

碼農談IT發表於2023-02-14

沒錯,Redis也有事務管理,但是功能很簡單,在正式開發中也並不推薦使用。但是面試中有可能會問到,所以本文簡單談一談Redis的事務。

透過這篇文章,你會了解

  • Redis為什麼要提供事務?
  • Redis事務基本指令和使用方法
  • CAS樂觀鎖是什麼?
  • Redis事務為什麼不支援回滾?

1. 為什麼要用事務

我們知道Redis的單個命令是原子性的,比如getsetmgetmset等指令。

原子性是指操作是不可分割的,在執行完畢之前不會被任何其它任務或事件中斷,也就不會有併發的安全性問題

在涉及到多個命令的時候,如果需要把多個命令設定為一個不可分割的處理序列,就需要用到事務了。

比如,招財和陀螺各有100元,招財給陀螺轉了10元,這時候需要在Redis中把招財的金額總數-10,同時需要把陀螺的金額總數+10。這兩個操作要麼同時成功,要麼同時失敗,這時候就需要事務了。

實際上,Redis連這個簡單的需求都沒辦法完美做到,至於為啥,接著往下看吧

2. 事務的用法

2.1 5個基本指令

Redis提供了以下5個基本指令,先混個眼熟就行,接下來在案例中進行實操,想記不住都難

  • MULTI
  • EXEC
  • DISCARD
  • WATCH
  • UNWATCH
雞肋的Redis事務

2.2 案例演示

案例場景:招財和陀螺各有100元,招財給陀螺轉了10元,這時候需要在Redis中把招財的金額-10,同時需要把陀螺的金額+10。

2.2.1 事務提交

我們首先為陀螺和招財初始化自己的金額;然後使用MULTI命令顯式開啟Redis事務。該命令總是直接返回OK。此時使用者可以傳送多個指令,Redis不會立刻執行這些命令,而是將這些指令依次放入當前事務的指令佇列中;EXEC被呼叫後,所有的命令才會被依次執行。

# 給陀螺初始化100元
127.0.0.1:6379> set tuoluo 100
OK
# 給招財初始化100元
127.0.0.1:6379> set zhaocai 100
OK
# 顯式開啟事務
127.0.0.1:6379> MULTI
OK
# 給陀螺增加10元
127.0.0.1:6379(TX)> INCRBY tuoluo 10
QUEUED
# 給招財減少10元
127.0.0.1:6379(TX)> DECRBY zhaocai 10
QUEUED
# 執行事務中的所有指令(提交事務)
127.0.0.1:6379(TX)> EXEC
1) (integer) 110
2) (integer) 90

2.2.2 巢狀事務

Redis不支援巢狀事務,多個MULTI命令和單個MULTI命令效果相同。

# 第一次開啟事務
127.0.0.1:6379> MULTI
OK
# 嘗試巢狀事務
127.0.0.1:6379(TX)> MULTI
(error) ERR MULTI calls can not be nested
# 仍然處於第一個事務當中
127.0.0.1:6379(TX)>

2.2.3 放棄事務

如果開啟事務之後,中途後悔了怎麼辦?呼叫DISCARD可以清空事務中的指令佇列,退出事務。

127.0.0.1:6379> MULTI
OK
# 在事務中呼叫DISCARD指令
127.0.0.1:6379(TX)> DISCARD
OK
# 會退出當前事務
127.0.0.1:6379>

2.2.4 watch指令

假如我們在一個客戶端連線中開啟了事務,另一個客戶端連線修改了這個事務涉及的變數值,將會怎樣?

雞肋的Redis事務

client1開啟了一個轉賬的事務,事務開始時招財和陀螺各自擁有100元,在執行EXEC指令之前,client2將陀螺的餘額新增了10元,此時執行EXEC之後,陀螺最終的金額為120元,招財為90元。

很明顯,這種情況下存在資料安全問題。

為此Redis提供了WATCH的指令,該指令可以為Redis事務提供CAS樂觀鎖行為,即多個連線同時更新變數的時候,會和變數的初始值進行比較,只在這個變數的值沒有被修改的情況下才會更新成新的值。

2.2.4.1 WATCH用法

對應我們的案例,我們可以使用WATCH監聽一個或多個key,如果開啟事務之前,至少有一個被監視的key在EXEC執行之前被修改了,那麼整個事務都會被取消,直接返回nil(見下面的案例)。UNWATCHWATCH的反操作。

雞肋的Redis事務

2.2.4.2 CAS機制

CAS(Compare And Swap)比較並替換,是多併發時常用的一種樂觀鎖技術

CAS需要三個變數資訊,分別是記憶體位置(JAVA中的記憶體地址,V),舊的預期值(A)和新值(B)。CAS執行時,當且僅當V和預期值A相等時,更新V的值為新值B,否則不執行更新。

雞肋的Redis事務

3. 事務執行出錯怎麼辦

事務執行時可能遇到問題,按照發生的時機不同分為兩種:

  • 執行EXEC之前
  • 執行EXEC之後

3.1 執行EXEC之前發生錯誤

比如指令存在語法錯誤(引數數量不對,指令單詞拼錯)導致不能進入commands佇列,這一步主要是編譯錯誤,還未到執行時。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> SET tuoluo
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)> EXEC
(error) EXECABORT Transaction discarded because of previous errors.

這種情況下事務會執行失敗,佇列中的所有指令都不會得到執行。

3.2 執行EXEC之後發生錯誤

這種錯誤往往是型別錯誤,比如對String使用了Hash的命令,這是執行時錯誤,編譯期間不會出錯

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> SET tuoluo 100
QUEUED
127.0.0.1:6379(TX)> LPOP tuoluo
QUEUED
127.0.0.1:6379(TX)> EXEC
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value

我們發現,SET tuoluo 100的命令居然執行成功了,也就是在發生了執行時異常的情況下,錯誤的指令不會被執行,但是其他的命令不會受影響。

雞肋的Redis事務

這種方式顯然不符合我們對原子性的定義,也就是Redis的事務無法實現原子性,無法保證資料一致。

針對這種缺陷,Redis官方也是做了說明的。

4. Redis事務為什麼不支援回滾

引自Redis官方文件。

雞肋的Redis事務

為了方便大家理解,我翻譯一下就是:

你們程式設計師的鍋,關我們Redis屁事兒!

Redis官方認為,只有在命令語法錯誤或者型別錯誤的時候,Redis命令才會執行失敗。而且他們認為有這種錯誤的語法一般也不會進入到生產環境。而且不支援回滾可以使他們有更多時間玩兒Redis執行得更簡單快捷。

這種說法多牛!如果出問題就是程式設計師的問題,寫錯了還讓程式碼進入生產環境,那就是罪上加罪,你永遠賴不著Redis官方。

這可能就是不推薦使用Redis事務的原因了吧,雞肋是一方面,萬一被官方打臉了呢?所以Redis事務的知識稍微瞭解一下就好,面試被問到能回到上來就可以了。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024924/viewspace-2935125/,如需轉載,請註明出處,否則將追究法律責任。

相關文章