Redis的事務、樂觀鎖和悲觀鎖

Future_LL發表於2019-01-28

一、是什麼

  • 可以一次執行多個命令,本質是一組命令的集合。
  • 一個事務中的所有命令都會序列化,按照順序地序列化執行而不會被其他命令插入,不許加塞

二、能幹嘛

  • 一個佇列中,一次性、順序性、排他性的執行一系列命令

三、怎麼玩

  • Redis中開啟事務的命令是:MULTI ,這個命令通常會回覆一個OK【回覆的是OK,但是這個事能不能辦,什麼時候辦,辦不辦的成不知道】,使用者將會一次性的打多個命令,而代替執行,按順序執行,Redis將這些命令入隊,所有的命令將會通過命令:EXEC 來被呼叫執行。
  • 如果用命令:DISCARD 表示放棄丟棄,言下之意是放棄本次的批處理操作
  • 常用命令:

    • DISCARD:取消事務,放棄執行事務塊內的所有命令
    • EXEC:執行所有事務塊內的命令
    • MULTI:標記一個事務塊的開始
    • UNWATCH:取消 WATCH 命令對所有 key 的監控
    • WATCH  key [key . . . ]:件事一個(或多個)key,如果在事務執行之前這個(或這些)key被其他命令所改動,那麼事務將被打斷
  • 正常執行:

  • 放棄事務:

  • 全體連坐:在事務塊中只要有一條命令執行是錯的,那麼整個事務塊就不會執行

  • 冤頭債主:如果在事務塊中所有命令都正確,但是結果會產生錯誤,那麼冤有頭債有主,誰錯找誰

  • watch監控:

    • 悲觀鎖
      •  悲觀鎖(Pessimistic Lock),顧名思義,就是很悲觀,每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,當其他執行緒想要訪問資料時,都需要阻塞掛起。傳統的關係型資料庫裡邊就用到了很多這種鎖機制,比如行鎖、表鎖,讀鎖,寫鎖等,都是在操作之前先上鎖。
      • 在Java中,synchronized的思想也是悲觀鎖。​​​​​​​
    • 樂觀鎖:【衝突檢測和資料更新
      • ​​​​​​​樂觀鎖(Optimistic Lock),顧名思義,就是很樂觀,每次去拿資料的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候回判斷一下再次期間別人有沒有去更新這個資料,可以使用版本號等機制。樂觀鎖適用於多讀的應用型別,這樣可以提高吞吐量。
      • 樂觀鎖策略:提交版本必須大於記錄當前版本才能執行更新
      • 一般會使用版本號機制或CAS操作實現:
        • version方式:一般是在資料表中加上一個資料版本號version欄位,表示資料被修改的次數,當資料被修改時,version值會加一。當執行緒A要更新資料值時,在讀取資料的同時也會讀取version值,在提交更新時,若剛才讀取到的version值為當前資料庫中的version值相等時才更新,否則重試更新操作,直到更新成功。
        • 核心SQL程式碼:

          update table set x=x+1, version=version+1 where id=#{id} and version=#{version};
    • ​​​​​​​​​​​​​​​​​​​​​CAS(Check And Set【先檢查再動手設定】)
      • ​​​​​​​CAS操作方式:即 compare and set,CAS是樂觀鎖技術,涉及到三個運算元,資料所在的記憶體值,預期值,新值。當需要更新時,判斷當前記憶體值與之前取到的值是否相等,若相等,則用新值更新,若失敗則重試,一般情況下是一個自旋操作,即不斷的重試。
    • ********************************************************************

    • *無加塞篡改,先監控再開啟 MULTI ,保證兩筆金額變動在同一個事務內*

    • ********************************************************************

    • 如下圖:就模擬了一個購物的過程,在買的過程中,別人會給你打錢,當你要完成支付的時候報錯了
    • 解決:使用 UNWATCH 取消對當前 key 的監控,之後再一次進行監控(得到最新的資料),直到成功為止
    • 注意:

    • **********************************************

    • 一旦執行了 EXEC,之前加的監控所都會被取消掉*

    • **********************************************

四、小結

  • 通過 WATCH 命令在事務執行之前監控了多個 Keys,倘若在 WATCH 之後有任何 Key 的值發生變化,EXEC 命令執行的事務都將被放棄,同時返回 Nullmulti-bulk 應答以通知呼叫者事務執行失敗

五、三階段

  • 開啟:以 MULTI 開始一個事務
  • 入隊:將多個命令入隊到事務中,接到這些命令並不會立即執行,而是放到等待執行的事務佇列裡面
  • 執行:由 EXEC 命令觸發事務

六、三特性

  • 單獨的隔離操作:事務中所有的命令多會序列化、按順序地執行。事務在執行的過程中,不會被其他客戶端傳送來的命令請求所打斷
  • 沒有隔離級別的概念:佇列中的命令沒有提交之前都不會實際的被執行,因為事務提交前任何指令都不會被實際的執行,也就是不存在 “ 事務內的查詢要看到事務裡面的更新,在事務外查詢不能看到 ” 這個是讓人萬分頭痛的問題
  • 不保證原子性:Redis 同一個事務中如果有一條命令執行失敗,其後的命令仍然會被執行,沒有回滾

相關文章