Redis命令參考手冊——Script(腳本)

Redis命令參考手冊——Script(腳本)

1、EVAL

格式:eval script numkeys key [key …] arg [arg …]
從 Redis 2.6.0 版本開始,通過內置的 Lua 解釋器,可以使用 EVAL 命令對 Lua 腳本進行求值。 script 參數是一段 Lua 5.1 腳本程序,它會被運行在 Redis 服務器上下文中,這段腳本不必(也不應該)定義爲一個 Lua 函數。 numkeys 參數用於指定鍵名參數的個數。 鍵名參數 key [key …] 從 EVAL 的第三個參數開始算起,表示在腳本中所用到的那些 Redis 鍵(key),這些鍵名參數可以在 Lua 中通過全局變量 KEYS 數組,用 1 爲基址的形式訪問( KEYS[1] , KEYS[2] ,以此類推)。 在命令的最後,那些不是鍵名參數的附加參數 arg [arg …] ,可以在 Lua 中通過全局變量 ARGV 數組訪問,訪問的形式和 KEYS 變量類似( ARGV[1] 、 ARGV[2] ,諸如此類)。 上面這幾段長長的說明可以用一個簡單的例子來概括:

> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first
second
1) "key1"
2) "key2"
3) "first"
4) "second"

其中 “return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}” 是被求值的 Lua 腳本,數字 2 指定了鍵名參數的數量, key1 和 key2 是鍵名參數,分別使用 KEYS[1] 和 KEYS[2] 訪問,而最後的 first 和 second 則是附加參數,可以通過 ARGV[1] 和 ARGV[2] 訪問它們。 在 Lua 腳本中,可以使用兩個不同函數來執行 Redis 命令,它們分別是: 1.redis.call() 2.redis.pcall() 這兩個函數的唯一區別在於它們使用不同的方式處理執行命令所產生的錯誤,在後面的『錯誤處理』部分會講到這一點。 redis.call() 和 redis.pcall() 兩個函數的參數可以是任何格式良好(well formed)的 Redis 命令:

> eval "return redis.call('set','foo','bar')" 0
OK

需要注意的是,上面這段腳本的確實現了將鍵 foo 的值設爲 bar 的目的,但是,它違反了 EVAL 命令的語義,因爲腳本里使用的所有鍵都應該由 KEYS 數組來傳遞,就像這樣:

> eval "return redis.call('set',KEYS[1],'bar')" 1 foo
OK

要求使用正確的形式來傳遞鍵(key)是有原因的,因爲不僅僅是 EVAL 這個命令,所有的 Redis 命令,在執行之前都會被分析,籍此來確定命令會對哪些鍵進行操作。 因此,對於 EVAL 命令來說,必須使用正確的形式來傳遞鍵,才能確保分析工作正確地執行。除此之外,使用正確的形式來傳遞鍵還有很多其他好處,它的一個特別重要的用途就是確保 Redis 集羣可以將你的請求發送到正確的集羣節點。(對 Redis 集羣的工作還在進行當中,但是腳本功能被設計成可以與集羣功能保持兼容。)不過,這條規矩並不是強制性的,從而使得用戶有機會濫用(abuse) Redis 單實例配置(single instance configuration),代價是這樣寫出的腳本不能被 Redis 集羣所兼容。

在Lua數據類型和Redis數據類型之間轉換

當 Lua 通過 call() 或 pcall() 函數執行 Redis 命令的時候,命令的返回值會被轉換成 Lua 數據結構。同樣地,當 Lua 腳本在 Redis 內置的解釋器裏運行時,Lua 腳本的返回值也會被轉換成 Redis 協議(protocol),然後由 EVAL 將值返回給客戶端。 數據類型之間的轉換遵循這樣一個設計原則:如果將一個 Redis 值轉換成 Lua 值,之後再將轉換所得的 Lua 值轉換回 Redis 值,那麼這個轉換所得的 Redis 值應該和最初時的 Redis 值一樣。 換句話說, Lua 類型和 Redis 類型之間存在着一一對應的轉換關係。 以下列出的是詳細的轉換規則: 從 Redis 轉換到 Lua : 1.Redis integer reply -> Lua number / Redis 整數轉換成 Lua 數字 2.Redis bulk reply -> Lua string / Redis bulk 回覆轉換成 Lua 字符串 3.Redis multi bulk reply -> Lua table (may have other Redis data types nested)/ Redis 多條 bulk 回覆轉換成 Lua 表,表內可能有其他別的 Redis 數據類型 4.Redis status reply -> Lua table with a single ok field containing the status/ Redis 狀態回覆轉換成 Lua 表,表內的 ok 域包含了狀態信息 5.Redis error reply -> Lua table with a single err field containing the error/ Redis 錯誤回覆轉換成 Lua 表,表內的 err 域包含了錯誤信息 6.Redis Nil bulk reply and Nil multi bulk reply -> Lua false boolean type /Redis 的 Nil 回覆和 Nil 多條回覆轉換成 Lua 的布爾值 false 從Lua轉換到Redis: 1.Lua number -> Redis integer reply / Lua 數字轉換成 Redis 整數 2.Lua string -> Redis bulk reply / Lua 字符串轉換成 Redis bulk 回覆 3.Lua table (array) -> Redis multi bulk reply / Lua 表(數組)轉換成 Redis 多條 bulk 回覆 4.Lua table with a single ok field -> Redis status reply / 一個帶單個 ok 域的 Lua 表,轉換成 Redis 狀態回覆 5.Lua table with a single err field -> Redis error reply / 一個帶單個 err 域的 Lua 表,轉換成 Redis 錯誤回覆 6.Lua boolean false -> Redis Nil bulk reply / Lua 的布爾值 false 轉換成 Redis的 Nil bulk 回覆 從 Lua 轉換到 Redis 有一條額外的規則,這條規則沒有和它對應的從 Redis 轉換到 Lua的規則: 1.Lua boolean true -> Redis integer reply with value of 1 / Lua 布爾值 true轉換成 Redis 整數回覆中的 1 以下是幾個類型轉換的例子:

> eval "return 10" 0
(integer) 10
> eval "return {1,2,{3,'Hello World!'}}" 0
1) (integer) 1
2) (integer) 2
3) 1) (integer) 3
2) "Hello World!"
> eval "return redis.call('get','foo')" 0
"bar"

在上面的三個代碼示例裏,前兩個演示瞭如何將 Lua 值轉換成 Redis 值,最後一個例子更復雜一些,它演示了一個將 Redis 值轉換成 Lua 值,然後再將 Lua 值轉換成 Redis值的類型轉過程。

腳本的原子性

Redis 使用單個 Lua 解釋器去運行所有腳本,並且, Redis 也保證腳本會以原子性(atomic)的方式執行:當某個腳本正在運行的時候,不會有其他腳本或 Redis 命令被執行。這和使用 MULTI / EXEC 包圍的事務很類似。在其他別的客戶端看來,腳本的效果(effect)要麼是不可見的(not visible),要麼就是已完成的(already completed)。 另一方面,這也意味着,執行一個運行緩慢的腳本並不是一個好主意。寫一個跑得很快很順溜的腳本並不難,因爲腳本的運行開銷(overhead)非常少,但是當你不得不使用一些跑得比較慢的腳本時,請小心,因爲當這些蝸牛腳本在慢吞吞地運行的時候,其他客戶端會因爲服務器正忙而無法執行命令。

錯誤處理

前面的命令介紹部分說過, redis.call() 和 redis.pcall() 的唯一區別在於它們對錯誤處理的不同。 當 redis.call() 在執行命令的過程中發生錯誤時,腳本會停止執行,並返回一個腳本錯誤,錯誤的輸出信息會說明錯誤造成的原因:

redis> lpush foo a
(integer) 1
redis> eval "return redis.call('get', 'foo')" 0
(error) ERR Error running script (call to
f_282297a0228f48cd3fc6a55de6316f31422f5d17): ERR Operation against
a key holding the wrong kind of value

和 redis.call() 不同, redis.pcall() 出錯時並不引發(raise)錯誤,而是返回一個帶 err 域的 Lua 表(table),用於表示錯誤:

redis 127.0.0.1:6379> EVAL "return redis.pcall('get', 'foo')" 0
(error) ERR Operation against a key holding the wrong kind of value

帶寬和EVALSHA

EVAL 命令要求你在每次執行腳本的時候都發送一次腳本主體(script body)。Redis 有一個內部的緩存機制,因此它不會每次都重新編譯腳本,不過在很多場合,付出無謂的帶寬來傳送腳本主體並不是最佳選擇。 爲了減少帶寬的消耗, Redis 實現了 EVALSHA 命令,它的作用和 EVAL 一樣,都用於對腳本求值,但它接受的第一個參數不是腳本,而是腳本的 SHA1 校驗和(sum)。 EVALSHA 命令的表現如下: 1.如果服務器還記得給定的 SHA1 校驗和所指定的腳本,那麼執行這個腳本 2.如果服務器不記得給定的 SHA1 校驗和所指定的腳本,那麼它返回一個特殊的錯誤,提醒用戶使用 EVAL 代替 EVALSHA 以下是示例:

> set foo bar
OK
> eval "return redis.call('get','foo')" 0
"bar"
> evalsha 6b1bf486c81ceb7edf3c093f4c48582e38c0e791 0
"bar"
> evalsha ffffffffffffffffffffffffffffffffffffffff 0
(error) `NOSCRIPT` No matching script. Please use
[EVAL](/commands/eval).

客戶端庫的底層實現可以一直樂觀地使用 EVALSHA 來代替 EVAL ,並期望着要使用的腳本已經保存在服務器上了,只有當 NOSCRIPT 錯誤發生時,才使用 EVAL 命令重新發送腳本,這樣就可以最大限度地節省帶寬。 這也說明了執行 EVAL 命令時,使用正確的格式來傳遞鍵名參數和附加參數的重要性:因爲如果將參數硬寫在腳本中,那麼每次當參數改變的時候,都要重新發送腳本,即使腳本的主體並沒有改變,相反,通過使用正確的格式來傳遞鍵名參數和附加參數,就可以在腳本主體不變的情況下,直接使用 EVALSHA 命令對腳本進行復用,免去了無謂的帶寬消耗。

腳本緩存

Redis 保證所有被運行過的腳本都會被永久保存在腳本緩存當中,這意味着,當 EVAL命令在一個 Redis 實例上成功執行某個腳本之後,隨後針對這個腳本的所有 EVALSHA 命令都會成功執行。 刷新腳本緩存的唯一辦法是顯式地調用 SCRIPT FLUSH 命令,這個命令會清空運行過的所有腳本的緩存。通常只有在雲計算環境中,Redis 實例被改作其他客戶或者別的應用程序的實例時,纔會執行這個命令。 緩存可以長時間儲存而不產生內存問題的原因是,它們的體積非常小,而且數量也非常少,即使腳本在概念上類似於實現一個新命令,即使在一個大規模的程序裏有成百上千的腳本,即使這些腳本會經常修改,即便如此,儲存這些腳本的內存仍然是微不足道的。 事實上,用戶會發現 Redis 不移除緩存中的腳本實際上是一個好主意。比如說,對於一個和 Redis 保持持久化鏈接(persistent connection)的程序來說,它可以確信,執行過一次的腳本會一直保留在內存當中,因此它可以在流水線中使用 EVALSHA 命令而不必擔心因爲找不到所需的腳本而產生錯誤(稍候我們會看到在流水線中執行腳本的相關問題)。

SCRIPT命令

Redis 提供了以下幾個 SCRIPT 命令,用於對腳本子系統(scripting subsystem)進行控制: 1.SCRIPT FLUSH:清除所有腳本緩存 2.SCRIPT EXISTS:根據給定的腳本校驗和,檢查指定的腳本是否存在於腳本緩存 3.SCRIPT LOAD:將一個腳本裝入腳本緩存,但並不立即運行它 4.SCRIPT KILL :殺死當前正在運行的腳本

純函數腳本

在編寫腳本方面,一個重要的要求就是,腳本應該被寫成純函數(pure function)。 也就是說,腳本應該具有以下屬性: 1.對於同樣的數據集輸入,給定相同的參數,腳本執行的 Redis 寫命令總是相同的。腳本執行的操作不能依賴於任何隱藏(非顯式)數據,不能依賴於腳本在執行過程中、或腳本在不同執行時期之間可能變更的狀態,並且它也不能依賴於任何來自 I/O設備的外部輸入。 使用系統時間(system time),調用像 RANDOMKEY 那樣的隨機命令,或者使用 Lua 的隨機數生成器,類似以上的這些操作,都會造成腳本的求值無法每次都得出同樣的結果。 爲了確保腳本符合上面所說的屬性, Redis 做了以下工作: 1.Lua 沒有訪問系統時間或者其他內部狀態的命令 2.Redis 會返回一個錯誤,阻止這樣的腳本運行: 這些腳本在執行隨機命令之後(比如 RANDOMKEY 、 SRANDMEMBER 或 TIME 等),還會執行可以修改數據集的 Redis命令。如果腳本只是執行只讀操作,那麼就沒有這一限制。注意,隨機命令並不一定就指那些帶 RAND 字眼的命令,任何帶有非確定性的命令都會被認爲是隨機命令,比如 TIME 命令就是這方面的一個很好的例子。 3.每當從 Lua 腳本中調用那些返回無序元素的命令時,執行命令所得的數據在返回給 Lua 之前會先執行一個靜默(slient)的字典序排序(lexicographicalsorting)。舉個例子,因爲 Redis 的 Set 保存的是無序的元素,所以在 Redis 命令行客戶端中直接執行 SMEMBERS ,返回的元素是無序的,但是,假如在腳本中執行 redis.call(“smembers”, KEYS[1]) ,那麼返回的總是排過序的元素。 4.對 Lua 的僞隨機數生成函數 math.random 和 math.randomseed 進行修改,使得每次在運行新腳本的時候,總是擁有同樣的 seed 值。這意味着,每次運行腳本時,只要不使用 math.randomseed ,那麼 math.random 產生的隨機數序列總是相同的。 儘管有那麼多的限制,但用戶還是可以用一個簡單的技巧寫出帶隨機行爲的腳本(如果他們需要的話)。 假設現在我們要編寫一個 Redis 腳本,這個腳本從列表中彈出 N 個隨機數。一個 Ruby 寫的例子如下:

require 'rubygems'
require 'redis'
r = Redis.new
RandomPushScript = < 0) do
res = redis.call('lpush',KEYS[1],math.random())
i = i-1
end
return res
EOF
r.del(:mylist)
puts r.eval(RandomPushScript,[:mylist],[10,rand(2**32)])

這個程序每次運行都會生成帶有以下元素的列表:

> lrange mylist 0 -1
1) "0.74509509873814"
2) "0.87390407681181"
3) "0.36876626981831"
4) "0.6921941534114"
5) "0.7857992587545"
6) "0.57730350670279"
7) "0.87046522734243"
8) "0.09637165539729"
9) "0.74990198051087"
10) "0.17082803611217"

上面的 Ruby 程序每次都只生成同樣的列表,用途並不是太大。那麼,該怎樣修改這個腳本,使得它仍然是一個純函數(符合 Redis 的要求),但是每次調用都可以產生不同的隨機元素呢? 一個簡單的辦法是,爲腳本添加一個額外的參數,讓這個參數作爲 Lua 的隨機數生成器的 seed 值,這樣的話,只要給腳本傳入不同的 seed ,腳本就會生成不同的列表元素。 以下是修改後的腳本:

RandomPushScript = < 0) do
res = redis.call('lpush',KEYS[1],math.random())
i = i-1
end
return res
EOF
r.del(:mylist)
puts r.eval(RandomPushScript,1,:mylist,10,rand(2**32))

儘管對於同樣的 seed ,上面的腳本產生的列表元素是一樣的(因爲它是一個純函數),但是隻要每次在執行腳本的時候傳入不同的 seed ,我們就可以得到帶有不同隨機元素的列表。 Seed 會在複製(replication link)和寫 AOF 文件時作爲一個參數來傳播,保證在載入AOF 文件或附屬節點(slave)處理腳本時, seed 仍然可以及時得到更新。 注意,Redis 實現保證 math.random 和 math.randomseed 的輸出和運行 Redis 的系統架構無關,無論是 32 位還是 64 位系統,無論是小端(little endian)還是大端(bigendian)系統,這兩個函數的輸出總是相同的。

全局變量保護

爲了防止不必要的數據泄漏進 Lua 環境, Redis 腳本不允許創建全局變量。如果一個腳本需要在多次執行之間維持某種狀態,它應該使用 Redis key 來進行狀態保存。 企圖在腳本中訪問一個全局變量(不論這個變量是否存在)將引起腳本停止, EVAL 命令會返回一個錯誤:

redis 127.0.0.1:6379> eval 'a=10' 0
(error) ERR Error running script (call to
f_933044db579a2f8fd45d8065f04a8d0249383e57): user_script:1: Script
attempted to create global variable 'a'

Lua 的 debug 工具,或者其他設施,比如打印(alter)用於實現全局保護的 meta table ,都可以用於實現全局變量保護。 實現全局變量保護並不難,不過有時候還是會不小心而爲之。一旦用戶在腳本中混入了Lua 全局狀態,那麼 AOF 持久化和複製(replication)都會無法保證,所以,請不要使用全局變量。 避免引入全局變量的一個訣竅是:將腳本中用到的所有變量都使用 local 關鍵字定義爲局部變量。

Redis 內置的 Lua 解釋器加載了以下 Lua 庫: 1.base 2.table 3.string 4.math 5.debug 6.cjson 7.cmsgpack 其中 cjson 庫可以讓 Lua 以非常快的速度處理 JSON 數據,除此之外,其他別的都是Lua 的標準庫。 每個 Redis 實例都保證會加載上面列舉的庫,從而確保每個 Redis 腳本的運行環境都是相同的。

使用腳本散發Redis日誌

在 Lua 腳本中,可以通過調用 redis.log 函數來寫 Redis 日誌(log): redis.log(loglevel, message) 其中, message 參數是一個字符串,而 loglevel 參數可以是以下任意一個值: 1.redis.LOG_DEBUG 2.redis.LOG_VERBOSE 3.redis.LOG_NOTICE 4.redis.LOG_WARNING 上面的這些等級(level)和標準 Redis 日誌的等級相對應。 對於腳本散發(emit)的日誌,只有那些和當前 Redis 實例所設置的日誌等級相同或更高級的日誌纔會被散發。 以下是一個日誌示例: redis.log(redis.LOG_WARNING, “Something is wrong with this script.”) 執行上面的函數會產生這樣的信息: [32343] 22 Mar 15:21:39 # Something is wrong with this script.

沙箱(sandbox)和最大執行時間

腳本應該僅僅用於傳遞參數和對 Redis 數據進行處理,它不應該嘗試去訪問外部系統(比如文件系統),或者執行任何系統調用。 除此之外,腳本還有一個最大執行時間限制,它的默認值是 5 秒鐘,一般正常運作的腳本通常可以在幾分之幾毫秒之內完成,花不了那麼多時間,這個限制主要是爲了防止因編程錯誤而造成的無限循環而設置的。 最大執行時間的長短由 lua-time-limit 選項來控制(以毫秒爲單位),可以通過編輯redis.conf 文件或者使用 CONFIG GET 和 CONFIG SET 命令來修改它。 當一個腳本達到最大執行時間的時候,它並不會自動被 Redis 結束,因爲 Redis 必須保證腳本執行的原子性,而中途停止腳本的運行意味着可能會留下未處理完的數據在數據集(data set)裏面。 因此,當腳本運行的時間超過最大執行時間後,以下動作會被執行: 1.Redis 記錄一個腳本正在超時運行 2.Redis 開始重新接受其他客戶端的命令請求,但是隻有 SCRIPT KILL 和 SHUTDOWNNOSAVE 兩個命令會被處理,對於其他命令請求, Redis 服務器只是簡單地返回BUSY 錯誤。 3.可以使用 SCRIPT KILL 命令將一個僅執行只讀命令的腳本殺死,因爲只讀命令並不修改數據,因此殺死這個腳本並不破壞數據的完整性 4.如果腳本已經執行過寫命令,那麼唯一允許執行的操作就是 SHUTDOWN NOSAVE ,它通過停止服務器來阻止當前數據集寫入磁盤

流水線(pipeline)上下文(context)中的EVALSHA

在流水線請求的上下文中使用 EVALSHA 命令時,要特別小心,因爲在流水線中,必須保證命令的執行順序。 一旦在流水線中因爲 EVALSHA 命令而發生 NOSCRIPT 錯誤,那麼這個流水線就再也沒有辦法重新執行了,否則的話,命令的執行順序就會被打亂。 爲了防止出現以上所說的問題,客戶端庫實現應該實施以下的其中一項措施: 1.總是在流水線中使用 EVAL 命令 2.檢查流水線中要用到的所有命令,找到其中的 EVAL 命令,並使用 SCRIPT EXISTS命令檢查要用到的腳本是不是全都已經保存在緩存裏面了。如果所需的全部腳本都可以在緩存裏找到,那麼就可以放心地將所有 EVAL 命令改成 EVALSHA 命令,否則的話,就要在流水線的頂端(top)將缺少的腳本用 SCRIPT LOAD 命令加上去。
可用版本:

>=2.6.0

時間複雜度:
EVAL 和 EVALSHA 可以在 O(1) 複雜度內找到要被執行的腳本,其餘的複雜度取決於執行的腳本本身。

2、EVALSHA

格式:evalsha shal numkeys key [key …] arg [arg …]
根據給定的 sha1 校驗碼,對緩存在服務器中的腳本進行求值。 將腳本緩存到服務器的操作可以通過 SCRIPT LOAD 命令進行。 這個命令的其他地方,比如參數的傳入方式,都和 EVAL 命令一樣。
可用版本:

>=2.6.0

時間複雜度:
根據腳本的複雜度而定。
示例代碼:

redis> SCRIPT LOAD "return 'hello moto'"
"232fd51614574cf0867b83d384a5e898cfd24e5a"
redis> EVALSHA "232fd51614574cf0867b83d384a5e898cfd24e5a" 0
"hello moto"

3、SCRIPT LOAD

格式:script load script
將腳本 script 添加到腳本緩存中,但並不立即執行這個腳本。 EVAL 命令也會將腳本添加到腳本緩存中,但是它會立即對輸入的腳本進行求值。 如果給定的腳本已經在緩存裏面了,那麼不做動作。 在腳本被加入到緩存之後,通過 EVALSHA 命令,可以使用腳本的 SHA1 校驗和來調用這個腳本。 腳本可以在緩存中保留無限長的時間,直到執行 SCRIPT FLUSH 爲止。 關於使用 Redis 對 Lua 腳本進行求值的更多信息,請參見 EVAL 命令。
可用版本:

>=2.6.0

時間複雜度:
O(N) , N 爲腳本的長度(以字節爲單位)。
返回值:
給定 script 的 SHA1 校驗和
示例代碼:

redis> SCRIPT LOAD "return 'hello moto'"
"232fd51614574cf0867b83d384a5e898cfd24e5a"
redis> EVALSHA 232fd51614574cf0867b83d384a5e898cfd24e5a 0
"hello moto"

4、SCRIPT EXISTS

格式:script exists script [script …]
給定一個或多個腳本的 SHA1 校驗和,返回一個包含 0 和 1 的列表,表示校驗和所指定的腳本是否已經被保存在緩存當中。 關於使用 Redis 對 Lua 腳本進行求值的更多信息,請參見 EVAL 命令。
可用版本:

>=2.6.0

時間複雜度:
O(N) , N 爲給定的 SHA1 校驗和的數量。
返回值:
一個列表,包含 0 和 1 ,前者表示腳本不存在於緩存,後者表示腳本已經在緩存裏面了。 列表中的元素和給定的 SHA1 校驗和保持對應關係,比如列表的第三個元素的值就表示第三個 SHA1 校驗和所指定的腳本在緩存中的狀態。
示例代碼:

redis> SCRIPT LOAD "return 'hello moto'" # 載入一個腳本
"232fd51614574cf0867b83d384a5e898cfd24e5a"
redis> SCRIPT EXISTS 232fd51614574cf0867b83d384a5e898cfd24e5a
1) (integer) 1
redis> SCRIPT FLUSH # 清空緩存
OK
redis> SCRIPT EXISTS 232fd51614574cf0867b83d384a5e898cfd24e5a
1) (integer) 0

5、SCRIPT KILL

格式:script kill
殺死當前正在運行的 Lua 腳本,當且僅當這個腳本沒有執行過任何寫操作時,這個命令才生效。 這個命令主要用於終止運行時間過長的腳本,比如一個因爲 BUG 而發生無限 loop 的腳本,諸如此類。 SCRIPT KILL 執行之後,當前正在運行的腳本會被殺死,執行這個腳本的客戶端會從EVAL 命令的阻塞當中退出,並收到一個錯誤作爲返回值。 另一方面,假如當前正在運行的腳本已經執行過寫操作,那麼即使執行 SCRIPT KILL ,也無法將它殺死,因爲這是違反 Lua 腳本的原子性執行原則的。在這種情況下,唯一可行的辦法是使用 SHUTDOWN NOSAVE 命令,通過停止整個 Redis 進程來停止腳本的運行,並防止不完整(half-written)的信息被寫入數據庫中。 關於使用 Redis 對 Lua 腳本進行求值的更多信息,請參見 EVAL 命令。
可用版本:

>=2.6.0

時間複雜度:
O(1)
返回值:
執行成功返回 OK ,否則返回一個錯誤。
示例代碼:

# 沒有腳本在執行時
redis> SCRIPT KILL
(error) ERR No scripts in execution right now.
# 成功殺死腳本時
redis> SCRIPT KILL
OK
(1.30s)
# 嘗試殺死一個已經執行過寫操作的腳本,失敗
redis> SCRIPT KILL
(error) ERR Sorry the script already executed write commands against
the dataset. You can either wait the script termination or kill the
server in an hard way using the SHUTDOWN NOSAVE command.
(1.69s)

以下是腳本被殺死之後,返回給執行腳本的客戶端的錯誤:

redis> EVAL "while true do end" 0
(error) ERR Error running script (call to
f_694a5fe1ddb97a4c6a1bf299d9537c7d3d0f84e7): Script killed by user
with SCRIPT KILL...
(5.00s)

6、SCRIPT FLUSH

清除所有 Lua 腳本緩存。 關於使用 Redis 對 Lua 腳本進行求值的更多信息,請參見 EVAL 命令。
可用版本:

>=2.6.0

時間複雜度:
O(N) , N 爲緩存中腳本的數量。
返回值:
總是返回 OK
示例代碼:

redis> SCRIPT FLUSH
OK
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章