Redis事務


  Redis事務以MULTI開始,中間添加多種命令,這些命令不會立即執行,而是被放入到一個隊列中,當執行EXEC時,隊列中的所有命令被依次執行。

  當命令放在MULTI中,但還未執行EXEC時,每個命令返回值為QUEUED,Redis事務將多個命令使用MULTI包括起來,調用EXEC一起執行,減少與客戶端之間通信往返次數,提升執行多個命令時的性能

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set key 1
QUEUED
127.0.0.1:6379> LPUSH list a
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) (integer) 1
127.0.0.1:6379> keys *
1) "list"
2) "key"
127.0.0.1:6379> 

  從2.6.5版本開始,在執行EXEC之前,redis命令在加入隊列時,如果出現錯誤(一般為語法錯誤),則執行EXEC時,該事務不會被執行,並自動丟棄

127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET flag 1
QUEUED
127.0.0.1:6379> LPUSH list a
QUEUED
127.0.0.1:6379> LPUSH list
(error) ERR wrong number of arguments for 'lpush' command
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> keys *
(empty list or set)  ## 正確的腳本也未執行,當前事務被丟棄
127.0.0.1:6379> 

  在2.6.5版本之前,執行MULTI之后,EXEC之前發生錯誤時,當執行EXEC命令,redis忽略掉錯誤命令,執行正確的命令。

  在EXEC執行之后發生錯誤,其他正確的命令會被執行,redis不會回滾

127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set mykey 1
QUEUED
127.0.0.1:6379> LPUSH mykey 12
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> keys *
1) "mykey"      ## 正確命令被執行,錯誤的命令被丟棄
127.0.0.1:6379> 

  Redis不支持事務回滾,官網給出了兩點理由

1、redis命令只有兩個錯誤會出現,一個為錯誤的語法結構,一個為對數據類型使用錯誤的方法處理,如上例中使用LPUSH操作字符串類型的mykey。這兩個問題在開發環境自測的時候就能夠發現。

2、redis內部的結構簡單,而且速度更快,因為它不需要回滾功能。

  DISCARD命令可用於終止當前事務並丟棄,也即是清空隊列中的命令。該命令必須應用在MULTI命令中

127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set key v
QUEUED
127.0.0.1:6379> set mykey 2
QUEUED
127.0.0.1:6379> LPUSH list aa
QUEUED
127.0.0.1:6379> DISCARD
OK
127.0.0.1:6379> EXEC
(error) ERR EXEC without MULTI     ## 已經不再事務范圍之內,也即是當前事務已經在執行DISCARD的時候結束
127.0.0.1:6379>

  以上為redis簡單的事務操作,但以上存在數據安全問題

  假設小王暑假打工賺了一百塊錢,存了起來,但由於小王所在地的一些方面的原因,錢放進去,但還未執行EXEC

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET money 100
QUEUED
127.0.0.1:6379> 

  此時小王媽媽去銀行給小王匯過來1000元,並且營業員服務周到,各種硬件完備,很快就辦完了業務

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set money 1000
QUEUED
127.0.0.1:6379> EXEC
1) OK
127.0.0.1:6379> get money
"1000"
127.0.0.1:6379> 

  此時小王的匯款也結束了,(執行了EXEC)

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET money 100
QUEUED
127.0.0.1:6379> EXEC
1) OK
127.0.0.1:6379> get money
"100"
127.0.0.1:6379> 

  平白無故丟了一千大洋,還得上十年學,十個暑假才能賺回來這些錢,很心塞。小王的問題也有辦法解決,那就是在redis 2.2及以后版本之后引入的WATCH命令

  小王再次存錢卡主

127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> WATCH money    ### 監控money
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set money 100
QUEUED

  小王的媽媽依然是去銀行轉錢給小王,效率依然杠杠的

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set money 1000
QUEUED
127.0.0.1:6379> EXEC
1) OK
127.0.0.1:6379> get money
"1000"
127.0.0.1:6379> 

  小王存的錢開始被寫入

127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> WATCH money
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set money 100
QUEUED
127.0.0.1:6379> EXEC
(nil)
127.0.0.1:6379> get money
"1000"
127.0.0.1:6379> 

  小王存錢沒有成功,查看了下余額,發現已經有1000大洋了,那么這一百塊就可以去揮霍了,吃飽喝足,網吧包夜走起。。。

  WATCH命令是一種樂觀鎖的實現,基於CAS(check and set,在java的JUC下atomic中,一些Atomic開頭的類,也使用了CAS原理,只不過在java中被稱為compare and set,但大致意思是一樣的)。watch用於監控某個key是否發生了改變,如果在一個事務中,某個key在設置參數之后,在執行exec之前,其他客戶端修改了該key,則該事務將返回null。

  1、watch必須與multi一起使用,才會發生作用,並且其必須在multi之前執行

  2、watch監控的元素在當前事務提交之前發生變化(另一個事務執行了exec),則無論當前事務中有多少命令,全部失敗。

  3、watch監控的元素在當前事務提交之前,被放入到另外一個事務的隊列中,但並未執行exec,則當前事務可正常提交

  4、watch監控的元素,被另外一個客戶端在非MULTI包括的命令中修改,則無論當前事務中有多少命令,也將全部失敗

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM