Redis中使用Lua指令碼的思路

來源:互聯網
上載者:User

標籤: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指令碼的思路

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.