Redis事務

概述

相信學過Mysql等其餘數據庫的同窗對事務這個詞都不陌生,事務表示的是一組動做,這組動做要麼所有執行,要麼所有不執行。爲何會有這樣的需求呢?看看下面的場景:java

  • 微博是一個弱關係型社交網絡,用戶之間有關注和被關注兩種關係,好比兩個用戶A和B,若是A關注B,則B的粉絲中就應該有A。關注這個動做須要兩個步驟完成:在A的關注者中添加B;在B的粉絲中添加A。 這兩個動做要麼都執行成功,要麼都不執行。不然就可能會出現A關注了B,可是B的粉絲中沒有A的不可容忍的狀況。
  • 轉帳匯款,假設如今有兩個帳戶A和B,如今須要將A中的一萬塊大洋轉到B的帳戶中,這個動做也須要兩個步驟完成:從A的帳戶中划走一萬塊;在B的帳戶中增長一萬塊。這兩個動做要麼所有執行成功,要麼所有不執行,不然自會有人問候你的!!!

Redis做爲一種高效的分佈式數據庫,一樣支持事務。redis

Redis事務

Redis中的事務(transaction)是一組命令的集合。事務同命令同樣都是Redis最小的執行單位,一個事務中的命令要麼都執行,要麼都不執行。Redis事務的實現須要用到 MULTI 和 EXEC 兩個命令,事務開始的時候先向Redis服務器發送 MULTI 命令,而後依次發送須要在本次事務中處理的命令,最後再發送 EXEC 命令表示事務命令結束。sql

舉個例子,使用redis-cli鏈接redis,而後在命令行工具中輸入以下命令:數據庫

1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set url http://qifuguang.me QUEUED 127.0.0.1:6379> set title winwill2012 QUEUED 127.0.0.1:6379> set desc java QUEUED 127.0.0.1:6379> EXEC 1) OK 2) OK 3) OK 127.0.0.1:6379> 127.0.0.1:6379> get url "http://qifuguang.me" 127.0.0.1:6379> get title "winwill2012" 127.0.0.1:6379> get desc "java" 127.0.0.1:6379> 

從輸出中能夠看到,當輸入MULTI命令後,服務器返回OK表示事務開始成功,而後依次輸入須要在本次事務中執行的全部命令,每次輸入一個命令服務器並不會立刻執行,而是返回」QUEUED」,這表示命令已經被服務器接受而且暫時保存起來,最後輸入EXEC命令後,本次事務中的全部命令纔會被依次執行,能夠看到最後服務器一次性返回了三個OK,這裏返回的結果與發送的命令是按順序一一對應的,這說明此次事務中的命令全都執行成功了。服務器

再舉個例子,在命令行工具中輸入以下命令:網絡

1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set a a QUEUED 127.0.0.1:6379> sett b b (error) ERR unknown command 'sett' 127.0.0.1:6379> set c c QUEUED 127.0.0.1:6379> EXEC (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> get a (nil) 127.0.0.1:6379> get b (nil) 127.0.0.1:6379> get c (nil) 127.0.0.1:6379> 

和前面的例子同樣,先輸入MULTI最後輸入EXEC表示中間的命令屬於一個事務,不一樣的是中間輸入的命令有一個錯誤(set寫成了sett),這樣由於有一個錯誤的命令致使事務中的其餘命令都不執行了(經過後續的get命令能夠驗證),可見事務中的全部命令式同呼吸共命運的。分佈式

若是客戶端在發送EXEC命令以前斷線了,則服務器會清空事務隊列,事務中的全部命令都不會被執行。而一旦客戶端發送了EXEC命令以後,事務中的全部命令都會被執行,即便此後客戶端斷線也不要緊,由於服務器已經保存了事務中的全部命令。工具

除了保證事務中的全部命令要麼全執行要麼全不執行外,Redis的事務還能保證一個事務中的命令依次執行而不會被其餘命令插入。試想一個客戶端A須要執行幾條命令,同時客戶端B發送了幾條命令,若是不使用事務,則客戶端B的命令有可能會插入到客戶端A的幾條命令中,若是想避免這種狀況發生,也可使用事務。ui

Redis事務錯誤處理

若是一個事務中的某個命令執行出錯,Redis會怎樣處理呢?要回答這個問題,首先要搞清楚是什麼緣由致使命令執行出錯:url

  1. 語法錯誤 就像上面的例子同樣,語法錯誤表示命令不存在或者參數錯誤
    這種狀況須要區分Redis的版本,Redis 2.6.5以前的版本會忽略錯誤的命令,執行其餘正確的命令,2.6.5以後的版本會忽略這個事務中的全部命令,都不執行,就好比上面的例子(使用的Redis版本是2.8的)

  2. 運行錯誤 運行錯誤表示命令在執行過程當中出現錯誤,好比用GET命令獲取一個散列表類型的鍵值。
    這種錯誤在命令執行以前Redis是沒法發現的,因此在事務裏這樣的命令會被Redis接受並執行。若是食物裏有一條命令執行錯誤,其餘命令依舊會執行(包括出錯以後的命令)。好比下例:

    1
    2 3 4 5 6 7 8 9 10 11 12 13 14 
    127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set key 1 QUEUED 127.0.0.1:6379> SADD key 2 QUEUED 127.0.0.1:6379> set key 3 QUEUED 127.0.0.1:6379> EXEC 1) OK 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 3) OK 127.0.0.1:6379> get key "3" 

    Redis中的事務並無關係型數據庫中的事務回滾(rollback)功能,所以使用者必須本身收拾剩下的爛攤子。不過因爲Redis不支持事務回滾功能,這也使得Redis的事務簡潔快速。

回顧上面兩種類型的錯誤,語法錯誤徹底能夠在開發的時候發現並做出處理,另外若是能很好地規劃Redis數據的鍵的使用,也是不會出現命令和鍵不匹配的問題的。

WATCH命令

從上面的例子咱們能夠看到,事務中的命令要所有執行完以後才能獲取每一個命令的結果,可是若是一個事務中的命令B依賴於他上一個命令A的結果的話該怎麼辦呢?就好比說實現相似Java中的i++的功能,先要獲取當前值,才能在當前值的基礎上作加一操做。這種場合僅僅使用上面介紹的MULTI和EXEC是不能實現的,由於MULTI和EXEC中的命令是一塊兒執行的,並不能將其中一條命令的執行結果做爲另外一條命令的執行參數,因此這個時候就須要引進Redis事務家族中的另外一成員:WATCH命令

換個角度思考上面說到的實現i++的方法,能夠這樣實現:

  1. 監控i的值,保證i的值不被修改
  2. 獲取i的原值
  3. 若是過程當中i的值沒有被修改,則將當前的i值+1,不然不執行

這樣就可以避免競態條件,保證i++可以正確執行。

WATCH命令能夠監控一個或多個鍵,一旦其中有一個鍵被修改(或刪除),以後的事務就不會執行,監控一直持續到EXEC命令(事務中的命令是在EXEC以後才執行的,EXEC命令執行完以後被監控的鍵會自動被UNWATCH)

舉個例子:

1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 
127.0.0.1:6379> set mykey 1 OK 127.0.0.1:6379> WATCH mykey OK 127.0.0.1:6379> set mykey 2 OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set mykey 3 QUEUED 127.0.0.1:6379> EXEC (nil) 127.0.0.1:6379> get mykey "2" 127.0.0.1:6379> 

上面的例子中,首先設置mykey的鍵值爲1,而後使用WATCH命令監控mykey,隨後更改mykey的值爲2,而後進入事務,事務中設置mykey的值爲3,而後執行EXEC運行事務中的命令,最後使用get命令查看mykey的值,發現mykey的值仍是2,也就是說事務中的命令根本沒有執行(由於WATCH監控mykey的過程當中,mykey被修改了,因此隨後的事務便會被取消)。

有了WATCH命令,咱們就能夠本身實現i++功能了,僞代碼以下:

1
2 3 4 5 6 7 8 9 10 11 
def incr($key):  WATCH $key  $value = GET $key  if not $value  $value = 0  $value = $value + 1   MULTI  SET $key $value  result = EXEC  return result[0] 

由於EXEC返回的是多行字符串,使用result[0]表示返回值的第一個字符串。

注意:因爲WATCH命令的做用只是當被監控的鍵被修改後取消以後的事務,並不能保證其餘客戶端不修改監控的值,因此當EXEC命令執行失敗以後須要手動從新執行整個事務。

執行EXEC命令以後會取消監控使用WATCH命令監控的鍵,若是不想執行事務中的命令,也可使用UNWATCH命令來取消監控。

相關文章
相關標籤/搜索