eval調用傳遞參數html
[root@base task]# redis-cli 127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second 1) "key1" 2) "key2" 3) "first" 4) "second" 127.0.0.1:6379>
其中2爲參數的redis鍵key的個數,key1,key2爲redis鍵key,first,second爲鍵名的附加參數。在lua腳本中經過KEYS和ARGV數組對腳本執行指定的參數進行訪問。redis
調用redis的set命令(方式一)數組
127.0.0.1:6379> eval "return redis.call('set','foo','bar')" 0 OK 127.0.0.1:6379> get foo "bar" 127.0.0.1:6379>
調用redis的set命令(方式二)緩存
127.0.0.1:6379> eval "return redis.call('set',KEYS[1],'bar')" 1 foo OK 127.0.0.1:6379> get foo "bar" 127.0.0.1:6379>
須要注意的是,上面這段腳本的確實現了將鍵
foo
的值設爲bar
的目的,可是,它違反了 EVAL 命令的語義,由於腳本里使用的全部鍵都應該由KEYS
數組來傳遞,就像這樣:bash要求使用正確的形式來傳遞鍵(key)是有緣由的,由於不只僅是 EVAL 這個命令,全部的 Redis 命令,在執行以前都會被分析,籍此來肯定命令會對哪些鍵進行操做。服務器
所以,對於 EVAL 命令來講,必須使用正確的形式來傳遞鍵,才能確保分析工做正確地執行。除此以外,使用正確的形式來傳遞鍵還有不少其餘好處,它的一個特別重要的用途就是確保 Redis 集羣能夠將你的請求發送到正確的集羣節點。(對 Redis 集羣的工做還在進行當中,可是腳本功能被設計成能夠與集羣功能保持兼容。)不過,這條規矩並非強制性的,從而使得用戶有機會濫用(abuse) Redis 單實例配置(single instance configuration),代價是這樣寫出的腳本不能被 Redis 集羣所兼容。雲計算
Lua數據類型和Redis數據類型存在的轉換關係以下:atom
如下列出的是詳細的轉換規則:lua
從 Redis 轉換到 Lua :spa
- Redis integer reply -> Lua number / Redis 整數轉換成 Lua 數字
- Redis bulk reply -> Lua string / Redis bulk 回覆轉換成 Lua 字符串
- Redis multi bulk reply -> Lua table (may have other Redis data types nested) / Redis 多條 bulk 回覆轉換成 Lua 表,表內可能有其餘別的 Redis 數據類型
- Redis status reply -> Lua table with a single ok field containing the status / Redis 狀態回覆轉換成 Lua 表,表內的
ok
域包含了狀態信息- Redis error reply -> Lua table with a single err field containing the error / Redis 錯誤回覆轉換成 Lua 表,表內的
err
域包含了錯誤信息- Redis Nil bulk reply and Nil multi bulk reply -> Lua false boolean type / Redis 的 Nil 回覆和 Nil 多條回覆轉換成 Lua 的布爾值
false
從 Lua 轉換到 Redis:
- Lua number -> Redis integer reply / Lua 數字轉換成 Redis 整數
- Lua string -> Redis bulk reply / Lua 字符串轉換成 Redis bulk 回覆
- Lua table (array) -> Redis multi bulk reply / Lua 表(數組)轉換成 Redis 多條 bulk 回覆
- Lua table with a single ok field -> Redis status reply / 一個帶單個
ok
域的 Lua 表,轉換成 Redis 狀態回覆- Lua table with a single err field -> Redis error reply / 一個帶單個
err
域的 Lua 表,轉換成 Redis 錯誤回覆- Lua boolean false -> Redis Nil bulk reply / Lua 的布爾值
false
轉換成 Redis 的 Nil bulk 回覆從 Lua 轉換到 Redis 有一條額外的規則,這條規則沒有和它對應的從 Redis 轉換到 Lua 的規則:
- Lua boolean true -> Redis integer reply with value of 1 / Lua 布爾值
true
轉換成 Redis 整數回覆中的1
Lua數據類型和Redis數據類型轉換
127.0.0.1:6379> eval "return {1,{1,'L','U','A'},0}" 0 1) (integer) 1 2) 1) (integer) 1 2) "L" 3) "U" 4) "A" 3) (integer) 0
腳本原子性的執行描述以下:
Redis 使用單個 Lua 解釋器去運行全部腳本,而且, Redis 也保證腳本會以原子性(atomic)的方式執行:當某個腳本正在運行的時候,不會有其餘腳本或 Redis 命令被執行。這和使用 MULTI / EXEC 包圍的事務很相似。在其餘別的客戶端看來,腳本的效果(effect)要麼是不可見的(not visible),要麼就是已完成的(already completed)。
另外一方面,這也意味着,執行一個運行緩慢的腳本並非一個好主意。寫一個跑得很快很順溜的腳本並不難,由於腳本的運行開銷(overhead)很是少,可是當你不得不使用一些跑得比較慢的腳本時,請當心,由於當這些蝸牛腳本在慢吞吞地運行的時候,其餘客戶端會由於服務器正忙而沒法執行命令。
redis.call()和redis.pcall()的區別以下:
當
redis.call()
在執行命令的過程當中發生錯誤時,腳本會中止執行,並返回一個腳本錯誤,錯誤的輸出信息會說明錯誤形成的緣由和
redis.call()
不一樣,redis.pcall()
出錯時並不引起(raise)錯誤,而是返回一個帶err
域的 Lua 表(table),用於表示錯誤
執行lua腳本時,能夠經過evalsha命令代替eval,按照正確的格式來傳遞參數,從而使腳本主體不變直接經過evalsha命令對腳本進行復用:
客戶端庫的底層實現能夠一直樂觀地使用 EVALSHA 來代替 EVAL ,並指望着要使用的腳本已經保存在服務器上了,只有當
NOSCRIPT
錯誤發生時,才使用 EVAL 命令從新發送腳本,這樣就能夠最大限度地節省帶寬。這也說明了執行 EVAL 命令時,使用正確的格式來傳遞鍵名參數和附加參數的重要性:由於若是將參數硬寫在腳本中,那麼每次當參數改變的時候,都要從新發送腳本,即便腳本的主體並無改變,相反,經過使用正確的格式來傳遞鍵名參數和附加參數,就能夠在腳本主體不變的狀況下,直接使用 EVALSHA 命令對腳本進行復用,免去了無謂的帶寬消耗。
Redis提供的腳本緩存:
Redis 保證全部被運行過的腳本都會被永久保存在腳本緩存當中,這意味着,當 EVAL 命令在一個 Redis 實例上成功執行某個腳本以後,隨後針對這個腳本的全部 EVALSHA 命令都會成功執行。
刷新腳本緩存的惟一辦法是顯式地調用
SCRIPT FLUSH
命令,這個命令會清空運行過的全部腳本的緩存。一般只有在雲計算環境中,Redis 實例被改做其餘客戶或者別的應用程序的實例時,纔會執行這個命令。緩存能夠長時間儲存而不產生內存問題的緣由是,它們的體積很是小,並且數量也很是少,即便腳本在概念上相似於實現一個新命令,即便在一個大規模的程序裏有成百上千的腳本,即便這些腳本會常常修改,即使如此,儲存這些腳本的內存仍然是微不足道的。
執行腳本時禁止使用Lua全局變量,避免變量數據泄漏到Lua執行環境中,從而使redis的AOF持久化和複製得不到保證。(注:腳本的執行的結果應該是隻對本次執行有效,若是腳本中引入Lua的全局變量,腳本屢次執行或者並行執行時則會出現問題)
避免引入全局變量的一個訣竅是:將腳本中用到的全部變量都使用
local
關鍵字定義爲局部變量。
執行超時腳本以下觀察redis返回忙等待提示
127.0.0.1:6379> eval "while (true) do redis.call('incr',KEYS[1]) end" 1 count
[root@base task]# redis-cli monitor (error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.
若是是在執行腳本前執行redis-cli -r -1 -i 1 get count(間隔從redis獲取count的值)時,以下的結果顯示了當默認配置5s事後,redis返回的BUSY忙等待響應
root@base redis-3.2.1]# redis-cli -r -1 -i 1 get count "524" "524" "524" "524" (error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE. (error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE. (error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE. (error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE. (error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.
redis.conf默認配置lua-time-limit
################# LUA SCRIPTING ########################
# Max execution time of a Lua script in milliseconds.
#
# If the maximum execution time is reached Redis will log that a script is
# still in execution after the maximum allowed time and will start to
# reply to queries with an error.
#
# When a long running script exceeds the maximum execution time only the
# SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be
# used to stop a script that did not yet called write commands. The second
# is the only way to shut down the server in the case a write command was
# already issued by the script but the user doesn't want to wait for the natural
# termination of the script.
#
# Set it to 0 or a negative value for unlimited execution without warnings.
lua-time-limit 5000
參考資料:
http://redisdoc.com/script/eval.html