標籤:lua redis
Redis提供了通過eval命令來執行Lua指令碼。下面通過幾個小例子來講述如何在Redis服務端執行Lua指令碼。
1. 執行Lua指令碼的幾個命令如下:
| 命令格式 |
說明 |
對應Jedis用戶端Jedis對象的方法之一(有更多重載方法) |
| EVAL script numkeys key [key ...] arg [arg ...] |
執行Lua指令碼 |
public Object eval(String script, int keyCount, String... params) |
| EVALSHA sha1 numkeys key [key ...] arg [arg ...] |
根據給定的 sha1 校正碼,對緩衝在伺服器中的指令碼進行求值 |
public Object evalsha(String sha1, int keyCount, String... params) |
| SCRIPT LOAD script |
將給定的指令碼緩衝,不執行,並返回sha1校正值 |
public String scriptLoad(String script) |
| SCRIPT EXISTS sha1 [sha1 ...] |
給定一個或多個指令碼的 SHA1 校正和,返回一個包含 0 和 1 的列表,表示校正和所指定的指令碼是否已經被儲存在緩衝當中 |
public List<Boolean> scriptExists(String... sha1) |
SCRIPT FLUSH
|
清除所有 Lua 指令碼緩衝 |
|
| SCRIPT KILL |
殺死當前正在啟動並執行 Lua 指令碼,若且唯若這個指令碼沒有執行過任何寫操作時,這個命令才生效(如果已經執行了寫操作,則需要通過shutdown nosave命令來處理) |
|
2.通過redis-cli用戶端執行Lua指令碼
redis-cli --eval myscript.lua key1 key2 , arg1 arg2 arg3
需要注意的是用逗號來分割key和參數,這裡與在互動式模式下執行evel命令有所不同。
3.實際案例
情境一:對一個特定請求1秒鐘只允許訪問10次,當符合請求訪問條件時,返回True,否則返回False。
Java用戶端操作Redis服務,實現代碼如下:
/** * 存取控制 * * 1秒內最多可訪問10次 * * @param key * @return */public boolean isAccess(String key) {String rkey = "acc:" + key;long value = jedis.incr(rkey);if (value == 1) {jedis.expire(rkey, 1);return true;} else {boolean rs = value <= 10;return rs;}}
INCR命令作為計數器,如果rkey存在,則增加1返回最終值,否則初始化值為0,然後加1。如上程式,如果訪問rkey不存在,則表示第一次請求,這時對其rkey設定到期時間為1秒,否則比較其值是否超過制定請求數的閥值10.
用Lua指令碼來完成這一操作:
--[[Judge status KEYS[1]:keyARGV[1]:request numbersARGV[2]:expires times seconds--]]local key, rqn, exp = KEYS[1], ARGV[1], ARGV[2];local value=redis.call("incr", key);redis.log(redis.LOG_NOTICE, "incr "..key);if(tonumber(value) == 1)then redis.call("expire", key, exp); redis.log(redis.LOG_NOTICE, "expire "..key.." "..exp) return true;else return tonumber(value) <= tonumber(rqn);end
通過Java用戶端代碼實現該功能存在一定缺陷,比如每1秒就需要操作1個incr和expire命令,並且該命令是由用戶端通過網路發起的,而使用Lua指令碼則既可以保證操作的原子性,又能使每次操作只需要一個key即可在伺服器端完成相應的判斷操作。可以通過SCRIPT LOAD的方式將指令碼緩衝到伺服器,通過sha1校正值+參數(Key,ARG)來執行,減輕網路傳輸,也對該功能做到較好的封裝。
情境二:指定模式key大量刪除
redis目前提供的刪除命令del僅支援刪除指定數量的key,並不能通過指定模式key來進行刪除,比如:del *user 刪除以user結尾的key。
在redis中提供了keys命令,該命令可以通過指定模式key來擷取key列表,下面通過keys和del命令組合實現一個指定模式key大量刪除的命令。
--[[Pattern delete keyKEYS[1]:pattern--]]redis.log(redis.LOG_NOTICE, "call keys "..KEYS[1]);local keys=redis.call("keys", KEYS[1]);local count = 0;if(keys and (table.maxn(keys) > 0)) then for index, key in ipairs(keys) do redis.log(redis.LOG_NOTICE, "del "..key); count = count + redis.call("del", key); endendreturn count;
需要注意的是情境二可以作為一種思路,通過Lua指令碼組合redis內建命令來實現特定功能的命令。而這裡的模式key大量刪除並未一個好的命令,因為如果key的數量很大時,將會有比較嚴重的效能問題。redis預設限制Lua指令碼執行時間最大為5秒,如果超過5秒將繼續接受來自用戶端的請求,並簡單的返回BUSY結果。這時候則需要SCRIPT KILL或者SHUTDOWN NOSAVE命令做相應的處理。因此應該儘力保證指令碼的執行速度極快。
情境三:產生隨機數
對於Redis而且,指令碼執行在相同資料集,相同參數下執行寫命令具有一致性的。其不依賴與隱式的資料集,指令碼執行過程中不同執行時期的狀態變化,也不依賴外部I/O裝置的輸入。
要符合Redis服務執行的指令碼條件,需要注意的地方比較多,可以參見: http://redis.io/commands/eval
下面是實現隨機數列表的Lua指令碼:
--[[Random lpush a list key-valueKEYS[1]:key nameARGV[1]:ramdom seed valueARGV[2]:add element count--]]math.randomseed(ARGV[1]);for i=1, ARGV[2], 1 do redis.call("lpush", KEYS[1], math.random());endredis.log(redis.LOG_NOTICE, "lpush " .. KEYS[1]);return true;
上述指令碼通過改變randomseed函數的參數來實現隨機數,如果兩次執行上述指令碼,ARGV[1]參數值相同,則產生的隨機數是相同的。
通過執行上述指令碼,記錄每次生產的值,然後刪除對應key,再次產生。
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M01/5B/FC/wKioL1UYz4WSgIhaAAOqB1w19Sc305.jpg" style="float:none;" title="randomlist.png" alt="wKioL1UYz4WSgIhaAAOqB1w19Sc305.jpg" />
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M01/5C/01/wKiom1UYzkqx3NzwAAFpvCYWbRo501.jpg" style="float:none;" title="randomlist2.png" alt="wKiom1UYzkqx3NzwAAFpvCYWbRo501.jpg" />
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M01/5C/02/wKiom1UYz6DwtZZyAAJG2oORc7c681.jpg" title="randomlist3.png" alt="wKiom1UYz6DwtZZyAAJG2oORc7c681.jpg" />
對比上述結果,在執行該指令碼時,隨機數的產生由seed參數(第一個參數)決定的。
相同隨機數種子下產生的隨機數是相同的,如果再次執行指令碼,指定產生的隨機數個數n小於已經產生的隨機數個數m,則取已經產生的前n個,如果指定產生的隨機數個數n大於已經產生的隨機數個數m,則次數再產生(n-m)個隨機數,並固定下來。
4.Redis中使用Lua指令碼總結
Redis內建了Lua解譯器,這為操作Redis伺服器和資料提供了巨大的靈活性。
文中幾個情境並不見得實際,有效,但並不能掩蓋Lua與Redis結合將為Redis的使用提供了更大的想象和操作空間。
我們可以通過Lua來實現更多特定功能的命令;用Lua來封裝複雜了Redis操作的業務;計數,統計,分析,收集資料;實現業務操作事務控制等等。更多情境,還需在實際中不斷摸索和嘗試。
本文出自 “野馬紅塵” 部落格,轉載請與作者聯絡!
Redis中使用Lua指令碼的思路