Redis watch 命令

在 Redis 中使用 watch 命令可以決定事務是執行還是回滾。一般而言,可以在 multi 命令之前使用 watch 命令監控某些鍵值對,然後使用 multi 命令開啓事務,執行各類對數據結構進行操作的命令,這個時候這些命令就會進入隊列。

當 Redis 使用 exec 命令執行事務的時候,它首先會去比對被 watch 命令所監控的鍵值對,如果沒有發生變化,那麼它會執行事務隊列中的命令,提交事務;如果發生變化,那麼它不會執行任何事務中的命令,而去事務回滾。無論事務是否回滾,Redis 都會去取消執行事務前的 watch 命令,這個過程如圖 1 所示。
在這裏插入圖片描述

Redis 參考了多線程中使用的 CAS(比較與交換,Compare And Swap)去執行的。在數據高併發環境的操作中,我們把這樣的一個機制稱爲樂觀鎖。這句話還是比較抽象,也不好理解。

所以先簡要論述其操作的過程,當一條線程去執行某些業務邏輯,但是這些業務邏輯操作的數據可能被其他線程共享了,這樣會引發多線程中數據不一致的情況。

爲了克服這個問題,首先,在線程開始時讀取這些多線程共享的數據,並將其保存到當前進程的副本中,我們稱爲舊值(old value),watch 命令就是這樣的一個功能。

然後,開啓線程業務邏輯,由 multi 命令提供這一功能。在執行更新前,比較當前線程副本保存的舊值和當前線程共享的值是否一致,如果不一致,那麼該數據已經被其他線程操作過,此次更新失敗。

爲了保持一致,線程就不去更新任何值,而將事務回滾;否則就認爲它沒有被其他線程操作過,執行對應的業務邏輯,exec 命令就是執行“類似”這樣的一個功能。

注意,“類似”這個字眼,因爲不完全是,原因是 CAS 原理會產生 ABA 問題。所謂 ABA 問題來自於 CAS 原理的一個設計缺陷,它可能引發 ABA 問題,如表 1 所示。
在這裏插入圖片描述

在處理複雜運算的時候,被線程 2 修改的 X 的值有可能導致線程 1 的運算出錯,而最後線程 2 將 X 的值修改爲原來的舊值 A,那麼到了線程 1 運算結束的時間順序 T6,它將檢測 X 的值是否發生變化,就會拿舊值 A 和當前的 X 的值 A 比對,結果是一致的,於是提交事務。

然後在複雜計算的過程中 X 被線程 2 修改過了,這會導致線程 1 的運算出錯。在這個過程中,對於線程 2 而言,X 的值的變化爲 A->B->A,所以 CAS 原理的這個設計缺陷被形象地稱爲“ABA 問題”。

僅僅記錄一箇舊值去比較是不足夠的,還要通過其他方法避免 ABA 問題。常見的方法如 Hibernate 對緩存的持久對象(PO)加入字段 version 值,當每次操作一次該 PO,則 version=version+1,這樣採用 CAS 原理探測 version 字段,就能在多線程的環境中,排除 ABA 問題,從而保證數據的一致性。

關於 CAS 和樂觀鎖的概念,本教程還會從更深層次討論它們,暫時討論到這裏,當討論完了 CAS 和樂觀鎖,讀者再回頭來看這個過程,就會有更深的理解了。

從上面的分析可以看出,Redis 在執行事務的過程中,並不會阻塞其他連接的併發,而只是通過比較 watch 監控的鍵值對去保證數據的一致性,所以 Redis 多個事務完全可以在非阻塞的多線程環境中併發執行,而且 Redis 的機制是不會產生 ABA 問題的,這樣就有利於在保證數據一致的基礎上,提高高併發系統的數據讀/寫性能。

下面演示一個成功提交的事務,如表 2 所示。

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章