標籤:函數調用 語言 uri redis 第一個 argv 清除 命令操作 val
Redis為什麼需要Lua指令碼的支援
當應用需要Redis完成一些Redis命令不支援的特性時,要麼擴充Redis client或者更甚至編寫c擴充Redis server。這都大大造成了應用的實現的難度。在此基礎上,Redis通過內建Lua解譯器,Redis client可以發起執行Lua指令碼,完成特殊的功能需求。
Redis中使用Lua指令碼
在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同理。
如:
> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second1) "key1"2) "key2"3) "first"4) "second"
上面返回key1和key2的value,並且返回連個參數。
Redis提供了兩個不同的Lua函數調用執行Redis命令:
- redis.call():當redis命令執行錯誤時,將會返回錯誤;
- redis.pcall:當redis命令執行錯誤時,將會捕獲異常,返回一個帶有錯誤域的Lua表;
如:
127.0.0.1:6380> lpush foo a(integer) 1127.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 value127.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 barOK127.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
Redis(六)Lua指令碼的支援