為了保證多條命令組合的原子性, Redis提供了簡單的事務功能以及集成Lua腳本來解決這個問題。
事務
事務表示一組動作, 要么全部執行, 要么全部不執行。
Redis提供了簡單的事務功能, 將一組需要一起執行的命令放到multi和exec兩個命令之間。
multi命令代表事務開始, exec命令代表事務結束,它們之間的命令是原子順序執行的。
如果要停止事務的執行, 可以使用discard命令代替exec命令即可。
如果事務中的命令出現錯誤, Redis的處理機制也不盡相同:
命令錯誤
該種類型屬於語法錯誤, 會造成整個事務無法執行, key和value的值未發生變化。
運行時錯誤
如命令寫錯了,誤把sadd寫為zadd,該種類型會執行成功,且不支持回滾。需要開發人員自己進行修復
有些應用場景需要在事務之前, 確保事務中的key沒有被其他客戶端修改過, 才執行事務, 否則不執行(類似樂觀鎖) 。 Redis提供了watch命令來解決這類問題(multi命令之前執行)。
Redis提供了簡單的事務, 之所以說它簡單, 主要是因為它不支持事務中的回滾特性, 同時無法實現命令之間的邏輯關系計算, 當然也體現了Redis的“keep it simple”的特性。
在Redis中使用Lua
在Redis中執行Lua腳本有兩種方法: eval和evalsha。
eval
用法:eval 腳本內容 key個數 key列表 參數列表 示例: > eval 'return "hello " .. KEYS[1] .. ARGV[1]' 1 redis world > "hello redisworld" 此時KEYS[1]="redis", ARGV[1]="world", 所以最終的返回結果是"hello redisworld"。 如果Lua腳本較長, 還可以使用redis-cli--eval直接執行文件。
eval命令執行Lua腳本過程
evalsha
除了使用eval, Redis還提供了evalsha命令來執行Lua腳本。 首先要將Lua腳本加載到Redis服務端, 得到該腳本的SHA1校驗和,evalsha命令使用SHA1作為參數可以直接執行對應Lua腳本, 避免每次發送Lua腳本的開銷。 這樣客戶端就不需要每次執行腳本內容, 而腳本也會常駐在服務端, 腳本功能得到了復用。
用法:
加載腳本: script load命令可以將腳本內容加載到Redis內存中,得到SHA1。 示例: # redis-cli script load "$(cat lua_get.lua)" "7413dc2440db1fea7c0a0bde841fa68eefaf149c" 執行腳本: evalsha的使用方法如下, 參數使用SHA1值, 執行邏輯和eval一致。 用法:evalsha 腳本SHA1值 key個數 key列表 參數列表 示例: # evalsha 7413dc2440db1fea7c0a0bde841fa68eefaf149c 1 redis world "hello redisworld"
使用evalsha執行Lua腳本過程
Lua腳本功能為Redis開發和運維人員帶來如下三個好處:
·Lua腳本在Redis中是原子執行的, 執行過程中間不會插入其他命令。
·Lua腳本可以幫助開發和運維人員創造出自己定制的命令, 並可以將這些命令常駐在Redis內存中, 實現復用的效果。
·Lua腳本可以將多條命令一次性打包, 有效地減少網絡開銷。
Redis管理Lua腳本相關命令
script load script 此命令用於將Lua腳本加載到Redis內存中。 script exists 判斷sha1是否已經加載到Redis內存中。 script flush 清除Redis內存已經加載的所有Lua腳本。 script kill 殺掉正在執行的Lua腳本。如果Lua腳本比較耗時, 甚至Lua腳本存在問題, 那么此時Lua腳本的執行會阻塞Redis, 直到腳本執行完畢或者外部進行干預將其結束。 (如果當前Lua腳本正在執行寫操作, 那么script kill將不會生效)
Redis Lua相關參數
lua-time-limit 默認5s,當Lua腳本時間超過lua-time-limit后, 向其他命令調用發送BUSY的信號, 但是並不會停止掉服務端和客戶端的腳本執行,
所以當達到lua-time-limit值之后, 其他客戶端在執行正常的命令時, 將會收到“Busy Redis is busy running a script”錯誤, 並且提示使用script kill或者shutdown nosave命令來殺掉這個busy的腳本