跟我一起學(xué)Redis之Redis事務(wù)簡單了解一下

前言

關(guān)系數(shù)據(jù)庫中的事務(wù),小伙伴們應(yīng)該是不陌生了,不管是在開發(fā)還是在面試過程中,總有兩個問題逃不掉:

  • 說說事務(wù)的特性;
  • 事務(wù)隔離級別是怎么一回事?

事務(wù)處理不好,數(shù)據(jù)就可能不準(zhǔn)確,最終就會導(dǎo)致業(yè)務(wù)出問題;借此機(jī)會簡單回顧一下事務(wù)特性及其隔離級別,就當(dāng)是復(fù)習(xí)了;

事務(wù)特性(ACID)

  • 原子性(Atomicity)

    指事務(wù)內(nèi)所有操作要么一起執(zhí)行成功,要么都一起失敗(或者說是回滾);如事務(wù)經(jīng)典轉(zhuǎn)賬案例:A給B轉(zhuǎn)賬,A把錢扣了,但B沒有收到;可見這種錯誤是不能接受的,最終會回滾,這也是原子性的重要性。

  • 一致性(Consistency)

    指事務(wù)執(zhí)行前后的狀態(tài)一致,如事務(wù)經(jīng)典轉(zhuǎn)賬案例:A給B互相轉(zhuǎn)賬,不管怎么轉(zhuǎn),最終兩者錢的總和還是不變;

  • 持久性(Durability)

    指事務(wù)一旦提交,數(shù)據(jù)就已經(jīng)永久保存了,不能再回滾;

  • 隔離性(Isolation)

    指多個并發(fā)事務(wù)之間的操作互不干擾,但是事務(wù)的并發(fā)可能會導(dǎo)致數(shù)據(jù)臟讀、不可重復(fù)讀、幻讀問題,根據(jù)業(yè)務(wù)情況,采用事務(wù)隔離級別進(jìn)行對應(yīng)數(shù)據(jù)讀問題處理。

事務(wù)隔離級別

  • 讀未提交(Read uncommitted)

    指一個事務(wù)讀取到其他未提交事務(wù)的數(shù)據(jù)??赡軐?dǎo)致數(shù)據(jù)臟讀。

    轉(zhuǎn)賬案例:A正在給B轉(zhuǎn)賬,本來轉(zhuǎn)的1000,A多輸入了個0,變成10000,但此事務(wù)還未提交,但此時B查詢到轉(zhuǎn)入的是10000,但A取消事務(wù)回滾之后,B又查詢不到轉(zhuǎn)入的數(shù)據(jù)。這種情況就是臟讀

  • 讀已提交(Read committed)

    指一個事務(wù)只能讀取到其他事務(wù)已提交的數(shù)據(jù),從而解決了臟讀的問題。但可能導(dǎo)致數(shù)據(jù)不可重復(fù)讀;

    轉(zhuǎn)賬案例:A要給B轉(zhuǎn)賬1000,A先查看了一下余額,有1000,然后開始給B轉(zhuǎn)錢,但此時A家里電費通過開啟的自動繳費功能,自動從A賬戶扣除200繳納電費,并提交;當(dāng)A轉(zhuǎn)賬準(zhǔn)備提交,再次確認(rèn)余額時,錢少了200。這樣就導(dǎo)致同一個事務(wù)中多次查詢的結(jié)果不一致,這種情況就是不可重復(fù)讀;

  • 可重復(fù)讀(Repeatable read)

    指事務(wù)只要一開啟,就不允許其他事務(wù)進(jìn)行修改操作,從而解決了不可重復(fù)讀問題。但可能導(dǎo)致數(shù)據(jù)幻讀;

    轉(zhuǎn)賬案例:A經(jīng)常給B轉(zhuǎn)賬,到年底了,需要查賬,然后開啟了一個事務(wù)進(jìn)行查詢統(tǒng)計,剛開始查詢只是10條轉(zhuǎn)賬記錄,正準(zhǔn)備統(tǒng)計時,因為緊急情況A需要給B轉(zhuǎn)一筆錢應(yīng)急,從而新增了一條新記錄,并提交;而查賬事務(wù)正在統(tǒng)計中,最后發(fā)現(xiàn)轉(zhuǎn)賬額和看到的10條轉(zhuǎn)賬記錄不匹配。這種情況就是幻讀

  • 序列化(Serializable )

    指事務(wù)之間只能串行話執(zhí)行,就像隊列一樣,排隊進(jìn)行,這樣就解決了幻讀的問題,但是這種級別的并發(fā)性能不高,非特殊需求,這種級別一般不用。

正文

轉(zhuǎn)入正題,結(jié)合關(guān)系型數(shù)據(jù)庫的事務(wù)來看看Redis中事務(wù)有什么不同;

Redis事務(wù)是指將多條命令加入隊列,一次批量執(zhí)行多條命令,每條命令會按順序執(zhí)行,事務(wù)執(zhí)行過程中不會受客戶端傳入的命令請求影響。

Redis事務(wù)的相關(guān)命令如下:

  • MULTI:標(biāo)識一個事務(wù)的開啟,即開啟事務(wù);
  • EXEC:執(zhí)行事務(wù)中的所有命令,即提交;
  • DISCARD:放棄事務(wù);和回滾不一樣,Redis事務(wù)不支持回滾。
  • WATCH:監(jiān)視Key改變,用于實現(xiàn)樂觀鎖。如果監(jiān)視的Key的值改變,事務(wù)最終會執(zhí)行失敗。
  • UNWATCH:放棄監(jiān)視。

Redis事務(wù)和關(guān)系型數(shù)據(jù)庫的事務(wù)不太一樣,它不保證原子性,也沒有隔離級別的概念。來,結(jié)合命令演示,實戰(zhàn)說明一切:

沒有隔離級別

image-20201112153132140

如上圖所示,當(dāng)事務(wù)開啟時,事務(wù)期間的命令并沒有執(zhí)行,而是加入隊列,只有執(zhí)行EXEC命令時,事務(wù)中的命令才會按照順序一一執(zhí)行,從而事務(wù)間就不會導(dǎo)致數(shù)據(jù)臟讀、不可重復(fù)讀、幻讀的問題,因此就沒有隔離級別

不保證原子性

image-20201112154524168

如上圖所示,在通過EXEC執(zhí)行事務(wù)時,其中命令執(zhí)行失敗不會影響到其他命令的執(zhí)行,并沒有保證同時成功和同時失敗的原子操作,盡管這樣,Redis事務(wù)中也沒有提供回滾的支持,官方提供了兩個理由:

image-20201112160255544

大概的意思就是:

  • 使用Redis命令語法錯誤,或是將命令運用在錯誤的數(shù)據(jù)類型鍵上(如對字符串進(jìn)行加減乘除等),從而導(dǎo)致業(yè)務(wù)數(shù)據(jù)有問題,這種情況認(rèn)為是編程導(dǎo)致的錯誤,應(yīng)該在開發(fā)過程中解決,避免在生產(chǎn)環(huán)境中發(fā)生;
  • 由于不用支持回滾功能,Redis內(nèi)部簡單化,而且還比較快;

在事務(wù)命令入隊過程中,發(fā)現(xiàn)相關(guān)命令邏輯使用錯誤,可以進(jìn)行放棄該事務(wù);如果使用錯誤的Redis命令,且沒有放棄事務(wù),最終也會導(dǎo)致事務(wù)整體執(zhí)行失敗,這也算是為原子性扳回一局,如下:

放棄事務(wù)

image-20201112223050653

命令語法錯誤導(dǎo)致事務(wù)執(zhí)行失敗

image-20201112223821761

使用WATCH實現(xiàn)樂觀鎖

說到樂觀鎖,就和悲觀鎖一起簡單說說對其的理解:

樂觀鎖:就是非常樂觀,做什么事都往好處想; 對于數(shù)據(jù)庫操作,就認(rèn)為每次操作數(shù)據(jù)的時候都認(rèn)為別的操作不會修改,所以不會加鎖,而是通過一個類似于版本的字段來標(biāo)識該數(shù)據(jù)是否修改過,在執(zhí)行本次操作前先判斷是否修改過,如果修改過就放棄本次操作重新再來;

悲觀鎖:就是非常悲觀,做什么事都覺得不好;對于數(shù)據(jù)庫操作,每次操作數(shù)據(jù)數(shù)據(jù)都會認(rèn)為別的操作會修改當(dāng)前數(shù)據(jù),說以都要對其進(jìn)行加鎖,類似于表鎖和行鎖。

WATCH通過監(jiān)視指定Redis Key,如果沒有改變,就執(zhí)行成功,如果發(fā)現(xiàn)對應(yīng)值發(fā)生改變,事務(wù)就會執(zhí)行失敗,如下圖;

image-20201112232612965

那會一直監(jiān)視指定的Key嗎?,答案當(dāng)然是不會的,以下三種方式可以取消監(jiān)視:

  • 事務(wù)執(zhí)行之后,不管是否執(zhí)行成功還好是失敗,都會取消對應(yīng)的監(jiān)視;
  • 當(dāng)監(jiān)視的客戶端斷開連接時,也會取消監(jiān)視;
  • 可以手動UNWATCH取消所有Key的監(jiān)視;

Redis事務(wù)優(yōu)缺點

優(yōu)點

  • 一次性按順序執(zhí)行多個Redis命令,不受其他客戶端命令請求影響;
  • 事務(wù)中的命令要么都執(zhí)行(命令間執(zhí)行失敗互相不影響),要么都不執(zhí)行(比如中間有命令語法錯誤);

缺點

  • 事務(wù)執(zhí)行時,不能保證原子性;
  • 命令入隊每次都需要和服務(wù)器進(jìn)行交互,增加帶寬;

注意

  • 當(dāng)事務(wù)中命令語法使用錯誤時,最終會導(dǎo)致事務(wù)執(zhí)行不成功,即事務(wù)內(nèi)所有命令都不執(zhí)行;
  • 當(dāng)事務(wù)中命令知識邏輯錯誤,就比如給字符串做加減乘除操作時,只能在執(zhí)行過程中發(fā)現(xiàn)錯誤,這種事務(wù)執(zhí)行中失敗的命令不影響其他命令的執(zhí)行。

總結(jié)

對于Redis事務(wù),其實用的不是很多,大部分喜歡使用Lua腳本進(jìn)行批量命令的執(zhí)行,同時還能保證命令執(zhí)行的原子性。

那為什么要說Redis事務(wù)呢?

在之前計劃寫這篇文章的時候,和一些朋友簡單溝通過,大家的確用的不多,基本上都是用Lua腳本;但面試會時不時遇到過Redis事務(wù)的問題,最常見的是Redis中的事務(wù)和關(guān)系型數(shù)據(jù)庫中的事務(wù)有什么區(qū)別,這是從面試角度出發(fā)有這篇文章;

其實Redis 2.6版本之前,還不支持Lua腳本時,Redis事務(wù)對于批量按序執(zhí)行命令的場景也是很用的;就拿當(dāng)下來說,如果一些業(yè)務(wù)需批量按序執(zhí)行命令的,同樣可以使用,并非一定要Lua腳本。這是從使用角度來說;

最后從學(xué)習(xí)角度來說,既然學(xué)Redis,就應(yīng)該盡可能的了解的多一點。 下一篇說說持久化。

一個被程序搞丑的帥小伙,關(guān)注"Code綜藝圈",跟我一起學(xué)~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容