最近在開發電商平台的子系統——儲值卡系統,系統核心業務涉及到金額消費以及庫存控制,因此為了解決建立在內存上高並發情況下的事務控制,使用了spring封裝的RedisTemplate執行lua腳本進行原子性操作,確保金額消費,庫存按順序處理,解決資源爭搶。
1.使用lua腳本
Redis 使用單個 Lua 解釋器去運行所有腳本,並且, Redis 也保證腳本會以原子性(atomic)的方式執行:當某個腳本正在運行的時候,不會有其他腳本或 Redis 命令被執行。這和使用 MULTI / EXEC 包圍的事務很類似。在其他別的客戶端看來,腳本的效果(effect)要么是不可見的(not visible),要么就是已完成的(already completed)。
另一方面,這也意味着,執行一個運行緩慢的腳本並不是一個好主意。寫一個跑得很快很順溜的腳本並不難,因為腳本的運行開銷(overhead)非常少,但是當你不得不使用一些跑得比較慢的腳本時,請小心,因為當這些蝸牛腳本在慢吞吞地運行的時候,其他客戶端會因為服務器正忙而無法執行命令。
2.實現watch機制
WATCH key [key ...]
監視一個(或多個) key ,如果在事務執行之前這個(或這些) key 被其他命令所改動,那么事務將被打斷。
MULTI
標記一個事務塊的開始。
事務塊內的多條命令會按照先后順序被放進一個隊列當中,最后由 EXEC 命令原子性(atomic)地執行。
EXEC
執行所有事務塊內的命令。
假如某個(或某些) key 正處於 WATCH 命令的監視之下,且事務塊中有和這個(或這些) key 相關的命令,那么 EXEC 命令只在這個(或這些) key 沒有被其 他命令所改動的情況下執行並生效,否則該事務被打斷(abort)。
例子:
# 事務被成功執行 redis> MULTI OK redis> INCR user_id QUEUED redis> INCR user_id QUEUED redis> INCR user_id QUEUED redis> PING QUEUED redis> EXEC 1) (integer) 1 2) (integer) 2 3) (integer) 3 4) PONG # 監視 key ,且事務成功執行 redis> WATCH lock lock_times OK
redis> MULTI OK redis> SET lock "huangz" QUEUED redis> INCR lock_times QUEUED redis> EXEC 1) OK 2) (integer) 1 # 監視 key ,且事務被打斷 redis> WATCH lock lock_times OK redis> MULTI OK redis> SET lock "joe" # 就在這時,另一個客戶端修改了 lock_times 的值 QUEUED redis> INCR lock_times QUEUED redis> EXEC # 因為 lock_times 被修改, joe 的事務執行失敗 (nil)