先看一下Redis官方對Lua腳本的解釋:https://redis.io/commands/eval
“Atomicity of scripts
Redis uses the same Lua interpreter to run all the commands. Also Redis guarantees that a script is executed in an atomic way: no other script or Redis command will be executed while a script is being executed. This semantic is similar to the one of MULTI / EXEC. From the point of view of all the other clients the effects of a script are either still not visible or already completed.”
大致翻譯:
“腳本原子性
Redis使用相同的Lua解釋器來運行所有的命令。Redis還保證腳本以原子方式執行:在執行腳本時,不會執行其他腳本或Redis命令。這個語義類似於MULTI/EXEC。從所有其他客戶端的角度來看,腳本的效果要麼仍然不可見,要麼已經完成。”
注意事項:
需要注意的是redis.call()函數出錯時會中斷腳本的執行,此時會出現一部分命令執行了,一部分沒有執行。測試如下:
採用的redis版本爲:5.0.7
準備數據:
127.0.0.1:6379> set haha haha
OK
127.0.0.1:6379> set key1 1
OK
127.0.0.1:6379> set key2 2
OK
執行一段中間調用redis.call()會出錯的Lua腳本,錯誤原因haha的key類型是字符串,執行HGET命令會出錯的:
127.0.0.1:6379> eval "redis.call('SET',KEYS[1],ARGV[1]);redis.call('HGET','haha',ARGV[1]);redis.call('SET',KEYS[2],ARGV[2]);return 1;" 2 key1 key2 11 22
(error) ERR Error running script (call to f_cf874476bb7116d92b3b0cbbf8b399ba853907d4): @user_script:1: WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> get key1
"11"
127.0.0.1:6379> get key2
"2"
127.0.0.1:6379> get haha
"haha"
127.0.0.1:6379>
可以看到key1被設置成了11,但是key2沒有變成22,因爲中間一步redis.call('HGET','haha',ARGV[1]);出錯中斷了腳本的執行。
redis.pcall()函數不會中斷Lua腳本的執行,比如接着上面執行腳本:
127.0.0.1:6379> eval "redis.pcall('SET',KEYS[1],ARGV[1]);redis.pcall('HGET','haha',ARGV[1]);redis.pcall('SET',KEYS[2],ARGV[2]);return 1;" 2 key1 key2 1 22
(integer) 1
127.0.0.1:6379> get haha
"haha"
127.0.0.1:6379> get key1
"1"
127.0.0.1:6379> get key2
"22"
127.0.0.1:6379>
所以爲了保證腳本的原子性,要謹慎使用redis.call()函數,如使用一定要確保這個函數的正確性。