redis的事務和watch

redis的事務

嚴格意義來講,redis的事務和我們理解的傳統數據庫(如mysql)的事務是不一樣的。

redis中的事務定義

Redis中的事務(transaction)是一組命令的集合。

事務同命令一樣都是Redis的最小執行單位,一個事務中的命令要么都執行,要么都不執行。
事務的原理是先將屬于一個事務的命令發送給Redis,然后再讓Redis依次執行這些命令。

Redis保證一個事務中的所有命令要么都執行,要么都不執行。如果在發送EXEC命令前客戶端斷線了,則Redis會清空事務隊列,事務中的所有命令都不會執行。而一旦客戶端發送了EXEC命令,所有的命令就都會被執行,即使此后客戶端斷線也沒關系,因為Redis中已經記錄了所有要執行的命令。

除此之外,Redis的事務還能保證一個事務內的命令依次執行而不被其他命令插入。試想客戶端A需要執行幾條命令,同時客戶端B發送了一條命令,如果不使用事務,則客戶端B的命令可能會插入到客戶端A的幾條命令中執行。如果不希望發生這種情況,也可以使用事務。

事務的應用

事務的應用非常普遍,如銀行轉賬過程中A給B匯款,首先系統從A的賬戶中將錢劃走,然后向B的賬戶增加相應的金額。這兩個步驟必須屬于同一個事務,要么全執行,要么全不執行。否則只執行第一步,錢就憑空消失了,這顯然讓人無法接受。

和傳統的事務不同

和傳統的mysql事務不同的事,即使我們的加錢操作失敗,我們也無法在這一組命令中讓整個狀態回滾到操作之前

事務的錯誤處理

如果一個事務中的某個命令執行出錯,Redis會怎樣處理呢?要回答這個問題,首先需要知道什么原因會導致命令執行出錯。

語法錯誤

語法錯誤指命令不存在或者命令參數的個數不對。比如:

redis>MULTI
OK
redis>SET key value
QUEUED
redis>SET key
(error)ERR wrong number of arguments for 'set' command
redis> errorCOMMAND key
(error) ERR unknown command 'errorCOMMAND'
redis> EXEC
(error) EXECABORT Transaction discarded because of previous errors.

跟在MULTI命令后執行了3個命令:一個是正確的命令,成功地加入事務隊列;其余兩個命令都有語法錯誤。而只要有一個命令有語法錯誤,執行EXEC命令后Redis就會直接返回錯誤,連語法正確的命令也不會執行。

這里需要注意一點:
Redis 2.6.5之前的版本會忽略有語法錯誤的命令,然后執行事務中其他語法正確的命令。就此例而言,SET key value會被執行,EXEC命令會返回一個結果:1) OK。

運行錯誤

運行錯誤指在命令執行時出現的錯誤,比如使用散列類型的命令操作集合類型的鍵,這種錯誤在實際執行之前Redis是無法發現的,所以在事務里這樣的命令是會被Redis接受并執行的。如果事務里的一條命令出現了運行錯誤,事務里其他的命令依然會繼續執行(包括出錯命令之后的命令),示例如下:

redis>MULTI
OK
redis>SET key 1
QUEUED
redis>SADD key 2
QUEUED
redis>SET key 3
QUEUED
redis>EXEC
1) OK
2) (error) ERR Operation against a key holding the wrong kind of value
3) OK
redis>GET key
"3"

可見雖然SADD key 2出現了錯誤,但是SET key 3依然執行了。

Redis的事務沒有關系數據庫事務提供的回滾(rollback)功能。為此開發者必須在事務執行出錯后自己收拾剩下的攤子(將數據庫復原回事務執行前的狀態等,這里我們一般采取日志記錄然后業務補償的方式來處理,但是一般情況下,在redis做的操作不應該有這種強一致性要求的需求,我們認為這種需求為不合理的設計)。

Watch命令

大家可能知道redis提供了基于incr命令來操作一個整數型數值的原子遞增,那么我們假設如果redis沒有這個incr命令,我們該怎么實現這個incr的操作呢?

那么我們下面的正主watch就要上場了。

如何使用watch命令

正常情況下我們想要對一個整形數值做修改是這么做的(偽代碼實現):

      val = GET mykey
      val = val + 1
      SET mykey $val

但是上述的代碼會出現一個問題,因為上面吧正常的一個incr(原子遞增操作)分為了兩部分,那么在多線程(分布式)環境中,這個操作就有可能不再具有原子性了。

研究過java的juc包的人應該都知道cas,那么redis也提供了這樣的一個機制,就是利用watch命令來實現的。

watch命令描述

WATCH命令可以監控一個或多個鍵,一旦其中有一個鍵被修改(或刪除),之后的事務就不會執行。監控一直持續到EXEC命令(事務中的命令是在EXEC之后才執行的,所以在MULTI命令后可以修改WATCH監控的鍵值)

利用watch實現incr

具體做法如下:

      WATCH mykey
      val = GET mykey
      val = val + 1
      MULTI
      SET mykey $val
      EXEC

和此前代碼不同的是,新代碼在獲取mykey的值之前先通過WATCH命令監控了該鍵,此后又將set命令包圍在事務中,這樣就可以有效的保證每個連接在執行EXEC之前,如果當前連接獲取的mykey的值被其它連接的客戶端修改,那么當前連接的EXEC命令將執行失敗。這樣調用者在判斷返回值后就可以獲悉val是否被重新設置成功。

注意點

由于WATCH命令的作用只是當被監控的鍵值被修改后阻止之后一個事務的執行,而不能保證其他客戶端不修改這一鍵值,所以在一般的情況下我們需要在EXEC執行失敗后重新執行整個函數。

執行EXEC命令后會取消對所有鍵的監控,如果不想執行事務中的命令也可以使用UNWATCH命令來取消監控。

實現一個hsetNX函數

我們實現的hsetNX這個功能是:僅當字段存在時才賦值

為了避免競態條件我們使用watch事務來完成這一功能(偽代碼):

    WATCH key  
    isFieldExists = HEXISTS key, field  
    if isFieldExists is 1  
    MULTI  
    HSET key, field, value  
    EXEC  
    else  
    UNWATCH  
    return isFieldExists

在代碼中會判斷要賦值的字段是否存在,如果字段不存在的話就不執行事務中的命令,但需要使用UNWATCH命令來保證下一個事務的執行不會受到影響。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容

  • 本文將從Redis的基本特性入手,通過講述Redis的數據結構和主要命令對Redis的基本能力進行直觀介紹。之后概...
    kelgon閱讀 61,222評論 23 625
  • 《Redis 入門指南》(第二版) 第一章 Redis 是什么 Redis (REmote Dictionary ...
    EdenPP閱讀 67,325評論 3 10
  • redis事務 Redis 通過 MULTI 、 DISCARD 、 EXEC 和 WATCH 四個命令來實現事務...
    全能程序猿閱讀 2,175評論 0 11
  • 本來準備好心情陪你 可是你又突然變卦 我是依賴友情沒錯 誰讓你圈子太大而我太小 誰讓我把自己看的那么低 誰讓我這么...
    當神經不再大條閱讀 426評論 0 1
  • 【體驗入】今天休息,給家里和老爸打了電話,家里和老爸說他們都很好,不用總擔心他們,反到是他們都說你自己出門在外的照...
    Lzr_2017閱讀 142評論 0 5