天天看點

Redis-11使用 watch 指令監控事務

文章目錄

  • ​​概述​​
  • ​​Redis watch流程​​
  • ​​ABA問題​​
  • ​​使用watch成功送出的事務的案例​​
  • ​​使用watch復原的事務的案例​​

概述

在 Redis 中使用 watch 指令可以決定事務是執行還是復原。

一般而言,可以在 multi 指令之前使用 watch 指令監控某些鍵值對,然後使用 multi 指令開啟事務,執行各類對資料結構進行操作的指令,這個時候這些指令就會進入隊列。

當 Redis 使用 exec 指令執行事務的時候,它首先會去比對被 watch 指令所監控的鍵值對,

  • 如果沒有發生變化,那麼它會執行事務隊列中的指令,送出事務;
  • 如果發生變化,那麼它不會執行任何事務中的指令,而去事務復原。

無論事務是否復原 , Redis 都會去取消執行事務前的 watch 指令

Redis watch流程

流程如下:

Redis-11使用 watch 指令監控事務

Redis 參考了多線程中使用的 CAS (比較與交換, Compare And Swap ) 去執行的。在

資料高并發環境的操作中,我們把這樣的一個機制稱為樂觀鎖.

ABA問題

先簡要論述其操作的過程:

當一條線程去執行某些業務邏輯,但是這些業務務邏輯操作的資料可能被其他線程共享了,這樣會引發多線程中資料不一緻的情況。為了克服這個問題,首先,線上程開始時讀取這些多線程共享的資料,并将其儲存到目前程序的副本中,我們稱為舊值( old value), watch 指令就是這樣的一個功能 。

然後,開啟線程業務邏輯,由 multi 指令提供這一功能。在執行更新前,比較目前線程副本儲存的舊值和目前線程共享的值是否一緻,如果不一緻,那麼該資料己經被其他線程操作過,此次更新失敗。為了保持一緻,線程就不去更新任何值,而将事務復原:否則就認為它沒有被其他線程操作過,執行對應的業務邏輯, exec 指令就是執行“類似”這樣的一個功能 。

注意,“類似”這個字眼,因為不完全是,原因是 CAS 原理會産生 ABA 問題。所謂ABA 問題來自于 CAS 原理的一個設計缺陷,它可能引發 ABA 問題

Redis-11使用 watch 指令監控事務

在處理複雜運算的時候,被線程 2 修改的 X 的值有可能導緻線程1的運算出錯,而最後線程 2 将 X 的值修改為原來的舊值 A,那麼到了線程 1運算結束的時間順序 T6,它将j檢測 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 問題,進而保證資料的一緻性。

Redis 在執行事務的過程中 , 并不會阻塞其他連接配接的并發,而隻是通過 比較 watch 監控的鍵值對去保證資料的一緻性 , 所 以 Redis 多個事務完全可 以在非阻塞的多線程環境中井發執行,而且 Redis 的機制是不會産生 ABA 問題的, 這樣就有利于在保證資料一緻的基礎上 , 提高高并發系統的資料讀/寫性能。

使用watch成功送出的事務的案例

時刻 用戶端 說明
T1 set key1 value1 初始化 key1
T2 watch key1 監控 key1 的健值對
T3 multi 開啟事務
T4 set key2 value2 設定 key2 的值
T5 exec 送出事務, Redis會在這個時間點檢測 key1 的值在 T2 時刻後,有沒有被其他指令修改泣,如果沒有 , 則送出事務去執行
127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> SET key1 value1
OK
127.0.0.1:6379> WATCH key1
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET key2 value2
QUEUED
127.0.0.1:6379> EXEC
1) OK
127.0.0.1:6379>      

這裡我們使用了 watch 指令設定了 一個 key1 的監控 , 然後開啟事務設定 key2 , 直至exec 指令去執行事務. 如果在目前會話中修改key1的值,也是可以成功的。

使用watch復原的事務的案例

時刻 用戶端1 用戶端2 說明
T1 set key1 value1 用戶端 1 :傳回 OK
T2 watch key1 用戶端 1 :監控 key1
T3 multi 用戶端 1: 開啟事務
T4 set key2 value2 用戶端1 : 事務指令入列
T5 set key1 val1 用戶端 2:修改 key1的值
T6 exec 用戶端 1:執行事務,但是事務會先檢查在 T2 時刻被監控的 key1 是否被其他指令修改過。因為客戶揣 2 修改過,是以它會復原事務,事實上如果用戶端執行的是 set key1 value1 指令,它也會認為 key1 被修改過,然後傳回( nil) ,是以是不會産生 ABA 問題的

用戶端一

127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> 
127.0.0.1:6379> SET key1 value1
OK
127.0.0.1:6379> WATCH key1
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set key2 value2
QUEUED

# 在這一步暫停下,打開第二個用戶端去修改key1的值,然後再exec
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379>      

用戶端二:

Redis-11使用 watch 指令監控事務

然後回到用戶端1 執行exec

Redis-11使用 watch 指令監控事務

繼續閱讀