Redis(六)Lua腳本的支持

###Redis爲何須要Lua腳本的支持 當應用須要Redis完成一些Redis命令不支持的特性時,要麼擴展Redis client或者更甚至編寫c擴展Redis server。這都大大形成了應用的實現的難度。在此基礎上,Redis經過內置Lua解釋器,Redis client能夠發起執行Lua腳本,完成特殊的功能需求。html

###Redis中使用Lua腳本redis

在Redis中能夠經過使用eval和evalsha命令提供對執行Lua的支持。數組

eval語法:緩存

EVAL script numkeys key [key ...] arg [arg ...]服務器

  • script是lua腳本;
  • numkeys是lua腳本中key的數量;
  • key是多選,即lua腳本中能夠操做多個key;
  • arg是多選,供lua基本執行時提供參數;

在lua腳本中能夠經過全局變量KEYS和ARGV獲取key和arg。KEYS和ARGV都是數組,KEYS[1],KEYS[2],...等等,基數從1開始,按照順序獲取後面的key。ARGV同理。函數

如:lua

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

上面返回key1和key2的value,而且返回連個參數。url

Redis提供了兩個不一樣的Lua函數調用執行Redis命令:.net

  • redis.call():當redis命令執行錯誤時,將會返回錯誤;
  • redis.pcall:當redis命令執行錯誤時,將會捕獲異常,返回一個帶有錯誤域的Lua表;

如:設計

127.0.0.1:6380>  lpush foo a
(integer) 1

127.0.0.1:6380> eval "return redis.call('get','foo')" 0
(error) ERR Error running script (call to f_6b1bf486c81ceb7edf3c093f4c48582e38c0e791): @user_script:1: WRONGTYPE Operation against a key holding the wrong kind of value

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

Lua腳本做爲一種腳本語言,有本身的數據類型,Redis也有本身的數據類型。在Lua腳本和Redis命令操做的之間切換時,必然會涉及數據類型的轉化。

數據類型之間的轉換遵循這樣一個設計原則:Redis調用Lua解釋器執行腳本時,會將Redis類型轉化成Lua類型;當Lua腳本執行後,返回值時,會將返回值轉化成Redis類型,eval再將Lua腳本返回給client。

具體的對應細節參考:Lua 數據類型和 Redis 數據類型之間轉換

###Redis中Lua腳本帶來的收益

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

緩存腳本:eval命令要求每次每次執行lua腳本時,都須要發送lua腳本至服務器,雖然redis緩存機制保證不會從新編譯lua腳本,可是每次都傳輸腳本主體,無疑是消耗帶框。爲了減小帶框,Redis使用evalsha命令發起lua腳本,可是evalsha的第一個參數不是lua腳本,而是腳本所對應的shasum校驗和值。

如:

127.0.0.1:6380> set foo bar
OK
127.0.0.1:6380> 
127.0.0.1:6380> 
127.0.0.1:6380> eval "return redis.call('get','foo')" 0
"bar"
127.0.0.1:6380> evalsha 6b1bf486c81ceb7edf3c093f4c48582e38c0e791 0
"bar"

客戶端庫的底層實現能夠一直樂觀地使用 EVALSHA 來代替 EVAL ,並指望着要使用的腳本已經保存在服務器上了,只有當 NOSCRIPT 錯誤發生時,才使用 EVAL 命令從新發送腳本,這樣就能夠最大限度地節省帶寬。

###Redis提供的SCRIPT命令

  • script flush:清除全部腳本緩存;
  • script exists:根據給定的腳本校驗和,判斷腳本是否存在;
  • script load:將一個腳本載入內存,可是不運行;
  • script kill:殺死一個正在執行的腳本程序;

######參考 https://redis.io/commands/eval

相關文章
相關標籤/搜索