深入淺出Redis(二)高級特性:事務

第一篇中介紹了Redis是一個強大的鍵-值倉儲,支持五種靈活的數據結構。事實上,Redis還支持其它的一些高級特性:事務、發佈與訂閱、管道、腳本等,本篇我們來看一下事務。


前一篇中我們提到,在Redis中每個命令都是原子性的,因爲Redis內部的實現是單線程的。當然Redis也支持多個命令之間的事務,不過事務在Redis中相對來說很簡單,不像數據庫事務那樣涉及傳播級別、隔離級別等特性。

使用multi命令開始一個新的事務,exec命令提交,discard命令回滾。如果把信用卡的可用額度存入balance,欠額存入debt,在消費的時候就必須在一個事務內同時更新這兩個鍵。
127.0.0.1:6379> set balance 100
OK
127.0.0.1:6379> set debt 0
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby balance 25
QUEUED
127.0.0.1:6379> incrby debt 25
QUEUED
127.0.0.1:6379> exec
1) (integer) 75
2) (integer) 25
127.0.0.1:6379> get balance
"75"
127.0.0.1:6379> get debt
"25"

在multi命令後的其它命令,返回結果都是"QUEUED“,這些命令不會立即執行,只是簡單的在Server端緩存起來了。在發出exec命令後,他們纔會被一起執行;或者discard命令回滾事務。

在Redis官方文檔有指出事務的兩個特點:
  1. 事務中的命令都是按着他們進入緩存隊列的順序依次執行的,在事務執行中,Redis不會受理其它客戶端的命令(隔離性)
  2. 事務中的命令,或者全部執行,或者全部不執行。(原子性)
上面的例子是信用卡扣減,但實際中在做扣減時我們需要檢查餘額是否足夠,所以一般會這麼做:
redis.multi()
balance = redis.get('balance')
if (balance < amtToSubtract) {
    redis.discard()
} else {
    redis.decrby('balance', amtToSubtract)
    redis.incrby('debt', amtToSubtract)
    redis.exec()
}

對於普通數據庫事務,上面的代碼沒問題,但對於Redis事務來說行不通,因爲在exec命令之前,所有的命令都被Redis緩存起來了,根本就拿不到balance的值。那類似這種需要基於已經存在的某個值的事務在Redis中如何實現呢?答案是Watch命令:
redis.watch('balance')
balance = redis.get('balance')
if (balance < amtToSubtract) {
    redis.unwatch()
} else {
    redis.multi()
    redis.decrby('balance', amtToSubtract)
    redis.incrby('debt', amtToSubtract)
    redis.exec()
}

通俗點講,watch命令就是標記一個鍵,如果標記了一個鍵,在提交事務前如果該鍵被別人修改過,那事務就會失敗,這種情況通常可以在程序中重新再嘗試一次。像上面的例子,首先標記了鍵balance,然後檢查餘額是否足夠,不足就取消標記,並不做扣減;足夠的話,就啓動事務進行更新操作,如果在此期間鍵balance被其它人修改,那在提交事務(執行exec)時就會報錯,程序中通常可以捕獲這類錯誤再重新執行一次,直到成功。

Redis事務失敗後不支持回滾
與數據庫事務很重要的一個區別是Redis事務在執行過程中出錯後不會回滾。在exec命令後,Redis Server開始一個個的執行被緩存的命令,如果其中某個命令執行出錯了,那之前的命令並不會被回滾。
127.0.0.1:6379> set value 1
OK
127.0.0.1:6379> set value2 abc
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr value
QUEUED
127.0.0.1:6379> incr value2
QUEUED
127.0.0.1:6379> exec
1) (integer) 2
2) (error) ERR value is not an integer or out of range
127.0.0.1:6379> get value
"2"
127.0.0.1:6379> 

exec提交事務後,在執行到incr value2時錯誤了(數據類型不正確),但事務對value的操作卻是生效的,這點可以從後面的get value的返回值看到。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章