Redis事务介绍
概述
相信學(xué)過(guò)Mysql等其他數(shù)據(jù)庫(kù)的同學(xué)對(duì)事務(wù)這個(gè)詞都不陌生,事務(wù)表示的是一組動(dòng)作,這組動(dòng)作要么全部執(zhí)行,要么全部不執(zhí)行。為什么會(huì)有這樣的需求呢?看看下面的場(chǎng)景:
- 微博是一個(gè)弱關(guān)系型社交網(wǎng)絡(luò),用戶之間有關(guān)注和被關(guān)注兩種關(guān)系,比如兩個(gè)用戶A和B,如果A關(guān)注B,則B的粉絲中就應(yīng)該有A。關(guān)注這個(gè)動(dòng)作需要兩個(gè)步驟完成:在A的關(guān)注者中添加B;在B的粉絲中添加A。 這兩個(gè)動(dòng)作要么都執(zhí)行成功,要么都不執(zhí)行。否則就可能會(huì)出現(xiàn)A關(guān)注了B,但是B的粉絲中沒(méi)有A的不可容忍的情況。
- 轉(zhuǎn)賬匯款,假設(shè)現(xiàn)在有兩個(gè)賬戶A和B,現(xiàn)在需要將A中的一萬(wàn)塊大洋轉(zhuǎn)到B的賬戶中,這個(gè)動(dòng)作也需要兩個(gè)步驟完成:從A的賬戶中劃走一萬(wàn)塊;在B的賬戶中增加一萬(wàn)塊。這兩個(gè)動(dòng)作要么全部執(zhí)行成功,要么全部不執(zhí)行,否則自會(huì)有人問(wèn)候你的!!!
Redis作為一種高效的分布式數(shù)據(jù)庫(kù),同樣支持事務(wù)。
Redis事務(wù)
Redis中的事務(wù)(transaction)是一組命令的集合。事務(wù)同命令一樣都是Redis最小的執(zhí)行單位,一個(gè)事務(wù)中的命令要么都執(zhí)行,要么都不執(zhí)行。Redis事務(wù)的實(shí)現(xiàn)需要用到?MULTI?和?EXEC?兩個(gè)命令,事務(wù)開(kāi)始的時(shí)候先向Redis服務(wù)器發(fā)送?MULTI?命令,然后依次發(fā)送需要在本次事務(wù)中處理的命令,最后再發(fā)送?EXEC?命令表示事務(wù)命令結(jié)束。
舉個(gè)例子,使用redis-cli連接redis,然后在命令行工具中輸入如下命令:
| 1 | 127.0.0.1:6379> MULTI | 
從輸出中可以看到,當(dāng)輸入MULTI命令后,服務(wù)器返回OK表示事務(wù)開(kāi)始成功,然后依次輸入需要在本次事務(wù)中執(zhí)行的所有命令,每次輸入一個(gè)命令服務(wù)器并不會(huì)馬上執(zhí)行,而是返回”QUEUED”,這表示命令已經(jīng)被服務(wù)器接受并且暫時(shí)保存起來(lái),最后輸入EXEC命令后,本次事務(wù)中的所有命令才會(huì)被依次執(zhí)行,可以看到最后服務(wù)器一次性返回了三個(gè)OK,這里返回的結(jié)果與發(fā)送的命令是按順序一一對(duì)應(yīng)的,這說(shuō)明這次事務(wù)中的命令全都執(zhí)行成功了。
再舉個(gè)例子,在命令行工具中輸入如下命令:
| 1 | 127.0.0.1:6379> MULTI | 
和前面的例子一樣,先輸入MULTI最后輸入EXEC表示中間的命令屬于一個(gè)事務(wù),不同的是中間輸入的命令有一個(gè)錯(cuò)誤(set寫(xiě)成了sett),這樣因?yàn)橛幸粋€(gè)錯(cuò)誤的命令導(dǎo)致事務(wù)中的其他命令都不執(zhí)行了(通過(guò)后續(xù)的get命令可以驗(yàn)證),可見(jiàn)事務(wù)中的所有命令式同呼吸共命運(yùn)的。
如果客戶端在發(fā)送EXEC命令之前斷線了,則服務(wù)器會(huì)清空事務(wù)隊(duì)列,事務(wù)中的所有命令都不會(huì)被執(zhí)行。而一旦客戶端發(fā)送了EXEC命令之后,事務(wù)中的所有命令都會(huì)被執(zhí)行,即使此后客戶端斷線也沒(méi)關(guān)系,因?yàn)榉?wù)器已經(jīng)保存了事務(wù)中的所有命令。
除了保證事務(wù)中的所有命令要么全執(zhí)行要么全不執(zhí)行外,Redis的事務(wù)還能保證一個(gè)事務(wù)中的命令依次執(zhí)行而不會(huì)被其他命令插入。試想一個(gè)客戶端A需要執(zhí)行幾條命令,同時(shí)客戶端B發(fā)送了幾條命令,如果不使用事務(wù),則客戶端B的命令有可能會(huì)插入到客戶端A的幾條命令中,如果想避免這種情況發(fā)生,也可以使用事務(wù)。
Redis事務(wù)錯(cuò)誤處理
如果一個(gè)事務(wù)中的某個(gè)命令執(zhí)行出錯(cuò),Redis會(huì)怎樣處理呢?要回答這個(gè)問(wèn)題,首先要搞清楚是什么原因?qū)е旅顖?zhí)行出錯(cuò):
-  語(yǔ)法錯(cuò)誤?就像上面的例子一樣,語(yǔ)法錯(cuò)誤表示命令不存在或者參數(shù)錯(cuò)誤 
 這種情況需要區(qū)分Redis的版本,Redis 2.6.5之前的版本會(huì)忽略錯(cuò)誤的命令,執(zhí)行其他正確的命令,2.6.5之后的版本會(huì)忽略這個(gè)事務(wù)中的所有命令,都不執(zhí)行,就比如上面的例子(使用的Redis版本是2.8的)
-  運(yùn)行錯(cuò)誤?運(yùn)行錯(cuò)誤表示命令在執(zhí)行過(guò)程中出現(xiàn)錯(cuò)誤,比如用GET命令獲取一個(gè)散列表類(lèi)型的鍵值。 
 這種錯(cuò)誤在命令執(zhí)行之前Redis是無(wú)法發(fā)現(xiàn)的,所以在事務(wù)里這樣的命令會(huì)被Redis接受并執(zhí)行。如果食物里有一條命令執(zhí)行錯(cuò)誤,其他命令依舊會(huì)執(zhí)行(包括出錯(cuò)之后的命令)。比如下例:1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14127.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中的事務(wù)并沒(méi)有關(guān)系型數(shù)據(jù)庫(kù)中的事務(wù)回滾(rollback)功能,因此使用者必須自己收拾剩下的爛攤子。不過(guò)由于Redis不支持事務(wù)回滾功能,這也使得Redis的事務(wù)簡(jiǎn)潔快速。 
回顧上面兩種類(lèi)型的錯(cuò)誤,語(yǔ)法錯(cuò)誤完全可以在開(kāi)發(fā)的時(shí)候發(fā)現(xiàn)并作出處理,另外如果能很好地規(guī)劃Redis數(shù)據(jù)的鍵的使用,也是不會(huì)出現(xiàn)命令和鍵不匹配的問(wèn)題的。
WATCH命令
從上面的例子我們可以看到,事務(wù)中的命令要全部執(zhí)行完之后才能獲取每個(gè)命令的結(jié)果,但是如果一個(gè)事務(wù)中的命令B依賴于他上一個(gè)命令A(yù)的結(jié)果的話該怎么辦呢?就比如說(shuō)實(shí)現(xiàn)類(lèi)似Java中的i++的功能,先要獲取當(dāng)前值,才能在當(dāng)前值的基礎(chǔ)上做加一操作。這種場(chǎng)合僅僅使用上面介紹的MULTI和EXEC是不能實(shí)現(xiàn)的,因?yàn)镸ULTI和EXEC中的命令是一起執(zhí)行的,并不能將其中一條命令的執(zhí)行結(jié)果作為另一條命令的執(zhí)行參數(shù),所以這個(gè)時(shí)候就需要引進(jìn)Redis事務(wù)家族中的另一成員:WATCH命令
換個(gè)角度思考上面說(shuō)到的實(shí)現(xiàn)i++的方法,可以這樣實(shí)現(xiàn):
- 監(jiān)控i的值,保證i的值不被修改
- 獲取i的原值
- 如果過(guò)程中i的值沒(méi)有被修改,則將當(dāng)前的i值+1,否則不執(zhí)行
這樣就能夠避免競(jìng)態(tài)條件,保證i++能夠正確執(zhí)行。
WATCH命令可以監(jiān)控一個(gè)或多個(gè)鍵,一旦其中有一個(gè)鍵被修改(或刪除),之后的事務(wù)就不會(huì)執(zhí)行,監(jiān)控一直持續(xù)到EXEC命令(事務(wù)中的命令是在EXEC之后才執(zhí)行的,EXEC命令執(zhí)行完之后被監(jiān)控的鍵會(huì)自動(dòng)被UNWATCH)
舉個(gè)例子:
| 1 | 127.0.0.1:6379> set mykey 1 | 
上面的例子中,首先設(shè)置mykey的鍵值為1,然后使用WATCH命令監(jiān)控mykey,隨后更改mykey的值為2,然后進(jìn)入事務(wù),事務(wù)中設(shè)置mykey的值為3,然后執(zhí)行EXEC運(yùn)行事務(wù)中的命令,最后使用get命令查看mykey的值,發(fā)現(xiàn)mykey的值還是2,也就是說(shuō)事務(wù)中的命令根本沒(méi)有執(zhí)行(因?yàn)閃ATCH監(jiān)控mykey的過(guò)程中,mykey被修改了,所以隨后的事務(wù)便會(huì)被取消)。
有了WATCH命令,我們就可以自己實(shí)現(xiàn)i++功能了,偽代碼如下:
| 1 | def incr($key): | 
因?yàn)镋XEC返回的是多行字符串,使用result[0]表示返回值的第一個(gè)字符串。
注意:由于WATCH命令的作用只是當(dāng)被監(jiān)控的鍵被修改后取消之后的事務(wù),并不能保證其他客戶端不修改監(jiān)控的值,所以當(dāng)EXEC命令執(zhí)行失敗之后需要手動(dòng)重新執(zhí)行整個(gè)事務(wù)。
執(zhí)行EXEC命令之后會(huì)取消監(jiān)控使用WATCH命令監(jiān)控的鍵,如果不想執(zhí)行事務(wù)中的命令,也可以使用UNWATCH命令來(lái)取消監(jiān)控。
聲明
原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處,本文鏈接:http://qifuguang.me/2015/09/30/Redis事務(wù)介紹/
如果你喜歡我的文章,請(qǐng)關(guān)注我的微信訂閱號(hào),更多干貨,第一時(shí)間與你分享:
轉(zhuǎn)載于:https://www.cnblogs.com/thrillerz/p/5036327.html
總結(jié)
 
                            
                        - 上一篇: 《秋槿》第十三句是什么
- 下一篇: 《需求分析与系统设计》读书笔记1
