redis執(zhí)行l(wèi)ua腳本的實(shí)現(xiàn)方法
從redis 2.6.0版本開始,redis內(nèi)置了Lua解釋器,并提供了eval命令來解析Lua腳本求值。
1. 語法格式
語法: eval script numkeys keys args
參數(shù): eval — redis提供解析lua腳本的命令
? ? ? ? ?script — lua腳本
? ? ? ? ? numkeys — 指定鍵名參數(shù)集(keys)的個數(shù)
? ? ? ? ? keys — 鍵名參數(shù)集,通過全局變量KEYS數(shù)組表示,起始下標(biāo)為1
? ? ? ? ? args — 鍵值參數(shù)集,通過全局變量ARGV數(shù)組表示,起始下標(biāo)為1
描述:? EVAL命令的語義要求字面量不要直接寫在lua腳本中,推薦使用變量來定義lua腳本,并將字面量放在鍵名參數(shù)集keys和鍵值參數(shù)集args中,通過全局變量KEYS和ARGV來獲取,這樣做的好處是可緩存!在lua腳本中,可以使用兩個函數(shù)來執(zhí)行redis命令,分別是:redis.call()和redis.pcall()
例子:
## 第一個eval命令,設(shè)置一個key=name,value=sym的字符串
eval "return redis.call('set',KEYS[1],ARGA[1])" 1 name sym
## 第二個eval命令:獲取key=name的字符串的值
eval "return redis.call('get',KEYS[1])" 1 name
錯誤處理:
上面說過,在lua腳本中可以使用call()和pcall()來執(zhí)行redis腳本,這兩個函數(shù)的效果是一模一樣的,唯一區(qū)別就是它們對于錯誤處理的不同:
①redis.call()在執(zhí)行命令中發(fā)生錯誤,腳本會停止執(zhí)行,返回一個腳本錯誤,錯誤的輸出信息會說明錯誤造成的原因:

②redis.pcall()執(zhí)行命令出錯時將捕獲錯誤并返回表示錯誤的Lua表類型

2.類型轉(zhuǎn)換
當(dāng) Lua 通過 call() 或 pcall() 函數(shù)執(zhí)行 Redis 命令的時候,命令的返回值會被轉(zhuǎn)換成 Lua 數(shù)據(jù)結(jié)構(gòu);同樣地,當(dāng)Lua腳本在 redis內(nèi)置解釋器里運(yùn)行時,Lua的返回值也會被轉(zhuǎn)換成Redis類型,然后由EVAL將值返回客戶端。lua類型與redis類型之間存在一一轉(zhuǎn)換的關(guān)系:
redis -> lua
|
redis類型 |
lua類型 |
描述 |
|
redis_integer |
lua_number |
redis整數(shù)轉(zhuǎn)為lua數(shù)字 |
|
redis_bulk |
lua_string |
redis bulk回復(fù)轉(zhuǎn)為lua字符串 |
|
redis_multi bulk |
Lua_table |
redis 多條bulk回復(fù)轉(zhuǎn)為Lua 表 |
|
redis_status |
lua_table |
redis狀態(tài)回復(fù)轉(zhuǎn)為lua表,表內(nèi)ok域包含狀態(tài)信息 |
|
redis_error |
lua_table |
redis錯誤回復(fù)轉(zhuǎn)為lua表,表內(nèi)的err域包含錯誤信息 |
|
redis_nil、 redis_multi nil |
lua_boolean_false |
redis的nil回復(fù)和nil多條回復(fù)轉(zhuǎn)為lua的布爾值false |
lua -> redis
|
lua類型 |
redis類型 |
描述 |
|
lua_number |
redis_integer |
lua數(shù)字轉(zhuǎn)為redis整數(shù) |
|
lua_string |
redis_bluk |
lua字符串轉(zhuǎn)為redis bulk回復(fù) |
|
lua_table、 lua_array |
redis_multi bulk |
lua表(數(shù)組)轉(zhuǎn)為redis多條bulk回復(fù) |
|
lua_table_ok |
redis status |
一個帶單個ok域的lua表,轉(zhuǎn)為redis 狀態(tài)回復(fù) |
|
lua_table_err |
redis_error |
一個帶單個err域的lua表,轉(zhuǎn)為redis 錯誤回復(fù) |
|
lua_boolean_false |
redis nil |
lua布爾值false轉(zhuǎn)為redis的nil回復(fù) |
從lua轉(zhuǎn)換到redis有一條額外的規(guī)則,這條規(guī)則沒有與其相對應(yīng)的redis轉(zhuǎn)換為lua的規(guī)則:
lua_boolean_true -> redis_integer_1,lua布爾值true轉(zhuǎn)為redis整數(shù)1

3.lua腳本
3.1 script命令
redis提供了以下幾個script命令,用于對于腳本子系統(tǒng)進(jìn)行控制:
script flush:清除所有的腳本緩存
script load:將腳本裝入腳本緩存,不立即運(yùn)行并返回其校驗(yàn)和
script exists:根據(jù)指定腳本校驗(yàn)和,檢查腳本是否存在于緩存
script kill:殺死當(dāng)前正在運(yùn)行的腳本(防止腳本運(yùn)行緩存,占用內(nèi)存)

3.2 腳本原子性
redis使用單個lua解釋器去運(yùn)行所有腳本,并且保證腳本會以原子性的方式去執(zhí)行,意味著當(dāng)某個腳本在運(yùn)行時,不會有其它腳本或者redis命令被執(zhí)行!所以,如果當(dāng)前腳本運(yùn)行很慢,服務(wù)器可能會因?yàn)檎Χ鵁o法執(zhí)行命令,如:


每個腳本都有一個最大執(zhí)行時間限制,默認(rèn)值是5s。最大執(zhí)行時間的長短由配置文件redis.conf的lua-time-limit選項(xiàng)來控制,或直接使用config get和config set命令來修改。當(dāng)一個腳本執(zhí)行達(dá)到最大執(zhí)行時間,redis不會主動結(jié)束它,它會進(jìn)行下面幾個步驟:
①redis記錄一個腳本正在超時運(yùn)行
②redis開始重新接受其它客戶端請求,但只接受執(zhí)行script kill命令和shutdown nosave兩個命令,若客戶端執(zhí)行其它命令,redis會返回busy錯誤。
③如果腳本只執(zhí)行過讀操作,使用script kill命令可以立即停止此腳本;如果腳本執(zhí)行過寫操作,只允許shutdown save/nosave命令,通過停止服務(wù)器來阻止當(dāng)前數(shù)據(jù)寫入磁盤。(此時服務(wù)器關(guān)閉,數(shù)據(jù)不會被保存)
? 
3.3 腳本緩存和EVALSHA
redis有一個內(nèi)部的腳本緩存機(jī)制,它不會每次都重新編譯腳本,反倒是它會將所有運(yùn)行過的腳本永久保存在腳本緩存中(因?yàn)閞edis發(fā)現(xiàn)腳本體積非常小,即使量很大,甚至經(jīng)常修改,儲存這些腳本的內(nèi)存也是微不足道的)。清空腳本緩存只有唯一一個方式,就是執(zhí)行script flush命令。使用eval命令執(zhí)行腳本時,每次都要發(fā)送腳本主體,如果腳本足夠復(fù)雜,這會付出無謂的網(wǎng)絡(luò)帶寬。redis基于對lua的緩存,它實(shí)現(xiàn)了evalsha命令。
evalsha命令和eval命令效果一樣,都是解釋lua腳本執(zhí)行,但是evalsha命令的第一個參數(shù)不是腳本主體,而是腳本的SHAI校驗(yàn)和,這個校驗(yàn)和可以通過script load命令得到。evalsha命令執(zhí)行過程分兩步:
①如果redis服務(wù)器保存了給定SHA1校驗(yàn)和所指定的腳本,就會執(zhí)行該腳本
②如果redis服務(wù)器沒保存給定SHA1校驗(yàn)和所指定的腳本,它就會返回一個特殊錯誤,告知客戶端使用eval命令去執(zhí)行
如下圖所示:
? 
3.4 全局變量保護(hù)
redis的lua腳本不允許創(chuàng)建全局變量,如果腳本需要在多次執(zhí)行之間維持某種狀態(tài),可以借助外部redis key來保存狀態(tài),每次腳本執(zhí)行前,獲取redis相對應(yīng)的key賦值給局部變量。在lua腳本中創(chuàng)建或訪問一個全局變量,都會引起腳本停止,eval命令會返回一個錯誤:

redis的全局變量保護(hù)并不是百分百成功,有時候會在腳本中混入lua全局狀態(tài),可能會引發(fā)AOF持久化和主從復(fù)制都無法得到保證。redis建議不要在腳本中使用全局變量,可以使用local關(guān)鍵字定義腳本中的變量!

3.5 日志記錄
在redis中使用腳本不會自動記錄日志,需要我們在腳本使用redis.log()手動保存日志信息。腳本保存的日志,只有那些與redis實(shí)例所設(shè)置的日志等級相同或更高級才會被記錄。語法:redis.log(loglevel,message)。其中,message表示要記錄的日志信息,是一個字符串;loglevel表示redis日志等級,有4個取值:
?? redis.LOG_DEBUG
?? redis.LOG_VERBOSE
?? redis.LOG_NOTICE
?? redis.LOG_WARNING
到此這篇關(guān)于redis執(zhí)行l(wèi)ua腳本的實(shí)現(xiàn)方法的文章就介紹到這了,更多相關(guān)redis執(zhí)行l(wèi)ua內(nèi)容請搜索本站以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持本站!
版權(quán)聲明:本站文章來源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請保持原文完整并注明來源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非maisonbaluchon.cn所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學(xué)習(xí)參考,不代表本站立場,如有內(nèi)容涉嫌侵權(quán),請聯(lián)系alex-e#qq.com處理。
關(guān)注官方微信