跟我一起学Redis之Redis事务简单了解一下
前言
關(guān)系數(shù)據(jù)庫中的事務(wù),小伙伴們應(yīng)該是不陌生了,不管是在開發(fā)還是在面試過程中,總有兩個問題逃不掉:
?說說事務(wù)的特性;?事務(wù)隔離級別是怎么一回事?
事務(wù)處理不好,數(shù)據(jù)就可能不準確,最終就會導致業(yè)務(wù)出問題;借此機會簡單回顧一下事務(wù)特性及其隔離級別,就當是復習了;
事務(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ā)可能會導致數(shù)據(jù)臟讀、不可重復讀、幻讀問題,根據(jù)業(yè)務(wù)情況,采用事務(wù)隔離級別進行對應(yīng)數(shù)據(jù)讀問題處理。
事務(wù)隔離級別
?讀未提交(Read uncommitted)指一個事務(wù)讀取到其他未提交事務(wù)的數(shù)據(jù)。可能導致數(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ù),從而解決了臟讀的問題。但可能導致數(shù)據(jù)不可重復讀;轉(zhuǎn)賬案例:A要給B轉(zhuǎn)賬1000,A先查看了一下余額,有1000,然后開始給B轉(zhuǎn)錢,但此時A家里電費通過開啟的自動繳費功能,自動從A賬戶扣除200繳納電費,并提交;當A轉(zhuǎn)賬準備提交,再次確認余額時,錢少了200。這樣就導致同一個事務(wù)中多次查詢的結(jié)果不一致,這種情況就是不可重復讀;?可重復讀(Repeatable read)指事務(wù)只要一開啟,就不允許其他事務(wù)進行修改操作,從而解決了不可重復讀問題。但可能導致數(shù)據(jù)幻讀;轉(zhuǎn)賬案例:A經(jīng)常給B轉(zhuǎn)賬,到年底了,需要查賬,然后開啟了一個事務(wù)進行查詢統(tǒng)計,剛開始查詢只是10條轉(zhuǎn)賬記錄,正準備統(tǒng)計時,因為緊急情況A需要給B轉(zhuǎn)一筆錢應(yīng)急,從而新增了一條新記錄,并提交;而查賬事務(wù)正在統(tǒng)計中,最后發(fā)現(xiàn)轉(zhuǎn)賬額和看到的10條轉(zhuǎn)賬記錄不匹配。這種情況就是幻讀?序列化(Serializable?)指事務(wù)之間只能串行話執(zhí)行,就像隊列一樣,排隊進行,這樣就解決了幻讀的問題,但是這種級別的并發(fā)性能不高,非特殊需求,這種級別一般不用。
正文
轉(zhuǎn)入正題,結(jié)合關(guān)系型數(shù)據(jù)庫的事務(wù)來看看Redis中事務(wù)有什么不同;
Redis事務(wù)是指將多條命令加入隊列,一次批量執(zhí)行多條命令,每條命令會按順序執(zhí)行,事務(wù)執(zhí)行過程中不會受客戶端傳入的命令請求影響。
Redis事務(wù)的相關(guān)命令如下:
?MULTI:標識一個事務(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如上圖所示,當事務(wù)開啟時,事務(wù)期間的命令并沒有執(zhí)行,而是加入隊列,只有執(zhí)行EXEC命令時,事務(wù)中的命令才會按照順序一一執(zhí)行,從而事務(wù)間就不會導致數(shù)據(jù)臟讀、不可重復讀、幻讀的問題,因此就沒有隔離級別。
不保證原子性:
image-20201112154524168
如上圖所示,在通過EXEC執(zhí)行事務(wù)時,其中命令執(zhí)行失敗不會影響到其他命令的執(zhí)行,并沒有保證同時成功和同時失敗的原子操作,盡管這樣,Redis事務(wù)中也沒有提供回滾的支持,官方提供了兩個理由:
image-20201112160255544
大概的意思就是:
?使用Redis命令語法錯誤,或是將命令運用在錯誤的數(shù)據(jù)類型鍵上(如對字符串進行加減乘除等),從而導致業(yè)務(wù)數(shù)據(jù)有問題,這種情況認為是編程導致的錯誤,應(yīng)該在開發(fā)過程中解決,避免在生產(chǎn)環(huán)境中發(fā)生;?由于不用支持回滾功能,Redis內(nèi)部簡單化,而且還比較快;
在事務(wù)命令入隊過程中,發(fā)現(xiàn)相關(guān)命令邏輯使用錯誤,可以進行放棄該事務(wù);如果使用錯誤的Redis命令,且沒有放棄事務(wù),最終也會導致事務(wù)整體執(zhí)行失敗,這也算是為原子性扳回一局,如下:
放棄事務(wù)
image-20201112223050653
命令語法錯誤導致事務(wù)執(zhí)行失敗
image-20201112223821761
使用WATCH實現(xiàn)樂觀鎖
說到樂觀鎖,就和悲觀鎖一起簡單說說對其的理解:
樂觀鎖:就是非常樂觀,做什么事都往好處想;對于數(shù)據(jù)庫操作,就認為每次操作數(shù)據(jù)的時候都認為別的操作不會修改,所以不會加鎖,而是通過一個類似于版本的字段來標識該數(shù)據(jù)是否修改過,在執(zhí)行本次操作前先判斷是否修改過,如果修改過就放棄本次操作重新再來;
悲觀鎖:就是非常悲觀,做什么事都覺得不好;對于數(shù)據(jù)庫操作,每次操作數(shù)據(jù)數(shù)據(jù)都會認為別的操作會修改當前數(shù)據(jù),所以都要對其進行加鎖,類似于表鎖和行鎖。
WATCH通過監(jiān)視指定Redis Key,如果沒有改變,就執(zhí)行成功,如果發(fā)現(xiàn)對應(yīng)值發(fā)生改變,事務(wù)就會執(zhí)行失敗,如下圖;
image-20201112232612965
那會一直監(jiān)視指定的Key嗎?,答案當然是不會的,以下三種方式可以取消監(jiān)視:
?事務(wù)執(zhí)行之后,不管是否執(zhí)行成功還好是失敗,都會取消對應(yīng)的監(jiān)視;?當監(jiān)視的客戶端斷開連接時,也會取消監(jiān)視;?可以手動UNWATCH取消所有Key的監(jiān)視;
Redis事務(wù)優(yōu)缺點
優(yōu)點:
?一次性按順序執(zhí)行多個Redis命令,不受其他客戶端命令請求影響;?事務(wù)中的命令要么都執(zhí)行(命令間執(zhí)行失敗互相不影響),要么都不執(zhí)行(比如中間有命令語法錯誤);
缺點:
?事務(wù)執(zhí)行時,不能保證原子性;?命令入隊每次都需要和服務(wù)器進行交互,增加帶寬;
注意
?當事務(wù)中命令語法使用錯誤時,最終會導致事務(wù)執(zhí)行不成功,即事務(wù)內(nèi)所有命令都不執(zhí)行;?當事務(wù)中命令知識邏輯錯誤,就比如給字符串做加減乘除操作時,只能在執(zhí)行過程中發(fā)現(xiàn)錯誤,這種事務(wù)執(zhí)行中失敗的命令不影響其他命令的執(zhí)行。
總結(jié)
對于Redis事務(wù),其實用的不是很多,大部分喜歡使用Lua腳本進行批量命令的執(zhí)行,同時還能保證命令執(zhí)行的原子性。
那為什么要說Redis事務(wù)呢?
在之前計劃寫這篇文章的時候,和一些朋友簡單溝通過,大家的確用的不多,基本上都是用Lua腳本;但面試會時不時遇到過Redis事務(wù)的問題,最常見的是Redis中的事務(wù)和關(guān)系型數(shù)據(jù)庫中的事務(wù)有什么區(qū)別,這是從面試角度出發(fā)有這篇文章;
其實Redis 2.6版本之前,還不支持Lua腳本時,Redis事務(wù)對于批量按序執(zhí)行命令的場景也是很用的;就拿當下來說,如果一些業(yè)務(wù)需批量按序執(zhí)行命令的,同樣可以使用,并非一定要Lua腳本。這是從使用角度來說;
最后從學習角度來說,既然學Redis,就應(yīng)該盡可能的了解的多一點。下一篇說說持久化。
一個被程序搞丑的帥小伙,關(guān)注"Code綜藝圈",跟我一起學~?
總結(jié)
以上是生活随笔為你收集整理的跟我一起学Redis之Redis事务简单了解一下的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微软发布VS Code Jupyter插
- 下一篇: ASP.NET Core 中基于工厂的中