Redis使用之Lua腳本

Lua腳本

Lua是一個高效的輕量級腳本語言,用標準C語言編寫并以源代碼形式開放, 其設計目的是為了嵌入應用程序中,從而為應用程序提供靈活的擴展和定制功能。

使用腳本的好處

  1. 減少網(wǎng)絡開銷,在Lua腳本中可以把多個命令放在同一個腳本中運行。
  2. 原子操作,Redis會將整個腳本作為一個整體執(zhí)行,中間不會被其他命令插入。換句話說,編寫腳本的過程中無需擔心會出現(xiàn)競態(tài)條件。
  3. 復用性,客戶端發(fā)送的腳本會永遠存儲在Redis中,這意味著其他客戶端可以復用這一腳本來完成同樣的邏輯。

Lua在Linux中的安裝

到官網(wǎng)下載lua的tar.gz的源碼包
tar -zxvf lua.tar.gz
進入解壓的目錄:
cd lua
make linux  (linux環(huán)境下編譯)
make install
如果報錯,說找不到readline/readline.h, 可以通過yum命令安裝
yum -y install readline-devel ncurses-devel
安裝完以后再make linux  / make install
最后,直接輸入 lua命令即可進入lua的控制臺

Redis與Lua

在Lua腳本中調用Redis命令,可以使用redis.call函數(shù)調用。比如我們調用string類型的命令。
redis.call(‘set’,’hello’,’world’)
redis.call 函數(shù)的返回值就是redis命令的執(zhí)行結果。redis.call函數(shù)會將這5種類型的返回值轉化對應的Lua的數(shù)據(jù)類型。

  • 從Lua腳本中獲得返回值
    在很多情況下我們都需要腳本可以有返回值,在腳本中可以使用return 語句將值返回給redis客戶端,通過return語句來執(zhí)行,如果沒有執(zhí)行return,默認返回為nil。
  • 如何在redis中執(zhí)行l(wèi)ua腳本
    Redis提供了EVAL命令可以使開發(fā)者像調用其他Redis內置命令一樣調用腳本。
    [EVAL] [腳本內容] [key參數(shù)的數(shù)量] [key …] [arg …]
    可以通過key和arg這兩個參數(shù)向腳本中傳遞數(shù)據(jù),他們的值可以在腳本中分別使用KEYS和ARGV 這兩個類型的全局變量訪問。比如我們通過腳本實現(xiàn)一個set命令,通過在redis客戶端中調用,那么執(zhí)行的語句是:
    lua腳本的內容為: return redis.call(‘set’,KEYS[1],ARGV[1]) //KEYS和ARGV必須大寫
    eval "return redis.call('set',KEYS[1],ARGV[1])" 1 hello world
    EVAL命令是根據(jù) key參數(shù)的數(shù)量-也就是上面例子中的1來將后面所有參數(shù)分別存入腳本中KEYS和ARGV兩個表類型的全局變量。當腳本不需要任何參數(shù)時也不能省略這個參數(shù)。如果沒有參數(shù)則為0
    eval "return redis.call(‘get’,’hello’)" 0
  • EVALSHA命令
    考慮到我們通過eval執(zhí)行l(wèi)ua腳本,腳本比較長的情況下,每次調用腳本都需要把整個腳本傳給redis,比較占用帶寬。為了解決這個問題,redis提供了EVALSHA命令允許開發(fā)者通過腳本內容的SHA1摘要來執(zhí)行腳本。該命令的用法和EVAL一樣,只不過是將腳本內容替換成腳本內容的SHA1摘要。
  1. Redis在執(zhí)行EVAL命令時會計算腳本的SHA1摘要并記錄在腳本緩存中。
  2. 執(zhí)行EVALSHA命令時Redis會根據(jù)提供的摘要從腳本緩存中查找對應的腳本內容,如果找到了就執(zhí)行腳本,否則返回“NOSCRIPT No matching script,Please use EVAL”。

通過案例來演示EVALSHA命令的效果

script load "return redis.call('get','hello')" 將腳本加入緩存并生成sha1命令
evalsha "a5a402e90df3eaeca2ff03d56d99982e05cf6574" 0
調用eval命令之前,先執(zhí)行evalsha命令,如果提示腳本不存在,則再調用eval命令。

Lua腳本實戰(zhàn)

需求:實現(xiàn)一個針對某個手機號的訪問頻次?

local num=redis.call('incr',KEYS[1])
if tonumber(num)==1 then
   redis.call('expire',KEYS[1],ARGV[1])
   return 1
elseif tonumber(num)>tonumber(ARGV[2]) then
   return 0
else
   return 1
end

執(zhí)行命令:./redis-cli --eval xxx.lua rate.limiting:13700000000 , 10 3
語法為: ./redis-cli –eval [lua腳本] [key…]空格,空格[args…]

腳本的原子性

Redis的腳本執(zhí)行是原子的,即腳本執(zhí)行期間Redis不會執(zhí)行其他命令。所有的命令必須等待腳本執(zhí)行完以后才能執(zhí)行。為了防止某個腳本執(zhí)行時間過程導致Redis無法提供服務。Redis提供了lua-time-limit參數(shù)限制腳本的最長運行時間。默認是5秒鐘。
當腳本運行時間超過這個限制后,Redis將開始接受其他命令但不會執(zhí)行(以確保腳本的原子性),而是返回BUSY的錯誤。

實踐操作

打開兩個客戶端窗口
在第一個窗口中執(zhí)行l(wèi)ua腳本的死循環(huán) eval “while true do end” 0
在第二個窗口中運行get hello
第二個窗口的運行結果是Busy, 可以通過script kill命令終止正在執(zhí)行的腳本。如果當前執(zhí)行的lua腳本對redis的數(shù)據(jù)進行了修改,比如(set)操作,那么script kill命令沒辦法終止腳本的運行,因為要保證lua腳本的原子性。如果執(zhí)行一部分終止了,就違背了這一個原則在這種情況下,只能通過 shutdown nosave命令強行終止。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 14,235評論 0 38
  • [TOC] 相關命令 EVAL SCRIPT_LOAD EVALSHA(執(zhí)行之前要求執(zhí)行過EVAL或者SCRIPT...
    志華_C閱讀 5,841評論 0 4
  • 在我還沒上中學的時候,我就常常聽父輩的人提起我們隔壁村子里曾經(jīng)發(fā)生過的一起詭異事件。按他們的口述,具體時間應該是發(fā)...
    杉沐柒閱讀 626評論 0 2
  • 沒什么可抱怨的 我覺得現(xiàn)在的自己必須熱血沸騰起來 沒有時間再讓我矯情 就像又一次高考 我沒理由再放任自己隨波逐流 ...
    祭憶__閱讀 199評論 0 0

友情鏈接更多精彩內容