柚子快報邀請碼778899分享:redis初級——Lua腳本
Lua腳本
1.簡介與用法
簡介
? Lua語言是在1993年由巴西一個大學研究小組發(fā)明,其設(shè)計目標是作為嵌入式程序移植到其他應(yīng)用程序,它是由C語言實現(xiàn)的,雖然簡單小巧但是功能強大,所以許多應(yīng)用都選用它作為腳本語言,尤其是在游戲領(lǐng)域,例如大名鼎鼎的暴雪公司將Lua語言引入到“魔獸世界”這款游戲中,Rovio公司將Lua語言作為“憤怒的小鳥”這款火爆游戲的關(guān)卡升級引擎,Web服務(wù)器Nginx將Lua語言作為擴展,增強自身功能。Redis將Lua作為腳本語言可幫助開發(fā)者定制自己的Redis命令,在這之前,必須修改源碼。
用法
數(shù)據(jù)結(jié)構(gòu)及其處理邏輯
Lua語言提供了如下幾種數(shù)據(jù)類型:booleans(布爾)、numbers(數(shù)值)、strings(字符串)、tables(表格),和許多高級語言相比相對簡單。下面將結(jié)合例子對Lua的基本數(shù)據(jù)類型和邏輯處理進行說明。
(1).字符串
-- 代碼示例
local strings val = "hello"
print(val)
--執(zhí)行結(jié)果:hello
print("world")
--執(zhí)行結(jié)果:world
其中l(wèi)ocal代表val變量是一個局部變量,如果沒有l(wèi)ocal修飾則代表是一個全局變量。
(2).數(shù)組
? 在Lua中,如果要使用類似于數(shù)組的功能,可以使用tables類型,下面代碼使用定義了一個tables類型的變量myArrays,但和大多數(shù)編程語言不同的是,在Lua中的數(shù)組下表從1開始計算。
-- 代碼示例
local tables myArrays = {"hello","world",true,88.0}
print(myArrays[1])
print(myArrays[2])
print(myArrays[3])
print(myArrays[4])
--執(zhí)行結(jié)果
-- hello
-- world
-- true
-- 88.0
如果想遍歷數(shù)據(jù)可以使用for或者while
for
下面代碼會計算1到100的和,關(guān)鍵字for以end作為結(jié)束符。
-- 代碼示例
local int sum = 0
for i = 1, 100
do
sum = sum + i
end
print(sum)
--執(zhí)行結(jié)果 5050
如果要遍歷數(shù)組,則首先需要知道數(shù)組的長度,只需要在變量前加一個#號即可
-- 代碼示例
local tables myArrays = {"hello","world",true,88.0}
for i=1,#myArrays
do
print(myArrays[i])
end
--執(zhí)行結(jié)果
-- hello
-- world
-- true
-- 88.0
除此之外Lua還提供了內(nèi)置函數(shù)ipairs,使用 for index, value in ipairs(tables)可以遍歷出所有的索引下標和值。
-- 代碼示例
local tables myArrays = {"hello","world",true,88.0}
for index, value in ipairs(myArrays)
do
print(index)
print(value)
end
--執(zhí)行結(jié)果
-- 1
-- hello
-- 2
-- world
-- 3
-- true
-- 4
-- 88.0
while
下面這段代碼同樣會計算1到100的和。只不過使用的是while循環(huán),while循環(huán)同樣以end為結(jié)束標記。
-- 代碼示例
local int sum = 0
local int i = 1
while i <= 100
do
sum = sum + i
i = i + 1
end
print(sum)
--執(zhí)行結(jié)果 5050
if else
要確定數(shù)組中存不存在world,有則打印true,沒有則打印false,我們可以使用if else語句
-- 代碼示例
local tables myArrays = {"hello","world",true,88.0}
local booleans ex = false
for index,value in ipairs(myArrays)
do
if value == "world"
then
ex = true
break
end
end
if ex == true
then
print("true")
else
print("false")
end
-- 執(zhí)行結(jié)果 true
(3).哈希
如果要使用類似于哈希的功能,同樣可以使用tables類型,例如下面代碼定義了一個tables,每個元素包含了key和value,其中strings1…strings2是將兩個字符串進行連接。
-- 代碼示例
local tables tom = {name = "tom",age = 18,height = 172.5}
print("姓名 : "..tom["name"]..",年齡 : "..tom["age"]..",身高 : "..tom["height"])
--執(zhí)行結(jié)果
-- 姓名 : tom,年齡 : 18,身高 : 172.5
如果要遍歷哈希,可以使用Lua的內(nèi)置函數(shù)pairs
-- 代碼示例
local tables tom = {name = "tom",age = 18,height = 172.5}
for key,value in pairs(tom)
do
print(key .. ":" .. value)
end
--執(zhí)行結(jié)果
-- height:172.5
-- age:18
-- name:tom
函數(shù)定義
? 在Lua中,函數(shù)以function開頭,以end結(jié)尾,funcName是函數(shù)名,中間部分是函數(shù)體。
-- function funcName()
-- ···
-- end
-- 例如寫一個contact方法用來拼接兩個字符串
function contact (str1 , str2)
return str1 .." ".. str2
end
print(contact("hello ","world"))
--執(zhí)行結(jié)果 hello world
2.Redis與Lua
在Redis中使用Lua
在Redis中執(zhí)行Lua腳本有兩種方法:eval 和 evalsha
(1) eval
eval 腳本內(nèi)容 key個數(shù) key列表 參數(shù)列表
下面例子使用了key列表和參數(shù)列表來為Lua腳本提供更多的靈活性:
> eval 'return "hello ".. KEYS[1] .. " " .. ARGV[1]' 1 redis world
"hello redis world"
此時KEYS[1] = redis,ARGV[1] = world,所以最終的打印結(jié)果為 hello redis world
注意:KEYS 和 ARGV 一定要大寫否則會報錯,如下
> eval 'return "hello ".. keys[1] .. " " .. argv[1]' 1 redis world
"ERR Error running script (call to f_9f158bb8946915295cd6c488611b5004b5bf66c9): @user_script:1: user_script:1: Script attempted to access nonexistent global variable 'keys'"
> eval 'return "hello ".. KEYS[1] .. " " .. argv[1]' 1 redis world
"ERR Error running script (call to f_6248f0862bc574fb60782a2d632d7db773e16286): @user_script:1: user_script:1: Script attempted to access nonexistent global variable 'argv'"
如果不需要傳KEYS或者ARGVS可以進行以下操作:
# 不傳KEYS
> eval 'return "hello ".. ARGV[1] .. " " .. ARGV[2]' 0 redis world
"hello redis world"
# 不傳ARGVS
> eval 'return "hello ".. KEYS[1] .. " " .. KEYS[2]' 2 redis world
"hello redis world"
如果Lua腳本過長,還可以直接使用redis-cli --eval 直接執(zhí)行Lua腳本文件。
eval命令和–eval參數(shù)本質(zhì)是一樣的,客戶端如果想執(zhí)行Lua腳本,首先在客戶端編寫好Lua腳本代碼,然后把腳本作為字符串發(fā)送給服務(wù)器,服務(wù)器端會將執(zhí)行結(jié)果返回給客戶端。
注意:這里的參數(shù)是需要使用逗號隔開的,逗號左右兩邊都需要有一個空格,沒有參數(shù)則不填。
[root@xxx src]# cat /root/myredis/script/contactStr.lua
return ARGV[1].." "..ARGV[2]
[root@xxx src]# redis-cli -a xxx --eval /root/myredis/script/contactStr.lua , hello world
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
"hello world"
整個過程如圖所示
(2) evalsha
? 除了使用eval,Redis還提供了evalsha命令來執(zhí)行Lua腳本。首先要將Lua腳本加載到Redis服務(wù)端,得到該腳本的SHA1校驗和,evalsha命令使用SHA1作為參數(shù)可以直接執(zhí)行對應(yīng)的Lua腳本,避免每次發(fā)送Lua腳本的開銷。這樣客戶端就不需要每次執(zhí)行腳本內(nèi)容,而腳本也會常駐在服務(wù)端,腳本功能得到了服用。
加載腳本:
? script load命令可以將腳本內(nèi)容加載到Redis內(nèi)存中,例如下面將contactStr.lua腳本加載到Redis中,得到SHA1為:9295ec8b57e8e4d0a3e89f6cb3ab5b225d2bcb34
# 代碼示例
[root@xxx src]# redis-cli -a xxx script load "$(cat /root/myredis/script/contactStr.lua)"
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
"9295ec8b57e8e4d0a3e89f6cb3ab5b225d2bcb34"
執(zhí)行腳本:
? evalsha的使用方法如下,參數(shù)使用SHA1值,執(zhí)行邏輯和eval一致。
evalsha 腳本SHA1值 key個數(shù) key列表 參數(shù)列表
# 代碼示例
[root@xxx src]# redis-cli -a xxx evalsha 9295ec8b57e8e4d0a3e89f6cb3ab5b225d2bcb34 0 hello world
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
"hello world"
Lua的Redis API
? Lua可以使用redis.call函數(shù)實現(xiàn)對Redis的訪問,例如下面的代碼是Lua使用redis.call調(diào)用了Redis的set和get操作:
-- 代碼示例
redis.call("set","hello","world")
redis.call("get","hello")
? 放在Redis的執(zhí)行效果如下:
# 代碼示例
> set hello world
"OK"
> eval 'return redis.call("get",KEYS[1])' 1 hello
"world"
? 除此之外Lua還可以使用redis.pcall函數(shù)實現(xiàn)對Redis的調(diào)用,redis.call和redis.pcall的不同在于,如果redis.call執(zhí)行失敗,那么腳本執(zhí)行結(jié)束會直接返回錯誤,而redis.pcall會忽略錯誤繼續(xù)執(zhí)行腳本。
3.Redis如何管理Lua
? Redis提供了4個命令實現(xiàn)對Lua腳本的管理。
(1) script load
? 此命令用于將Lua腳本加載到Redis內(nèi)存中
# 代碼示例
[root@xxx src]# redis-cli -a xxx script load "$(cat /root/myredis/script/contactStr.lua)"
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
"9295ec8b57e8e4d0a3e89f6cb3ab5b225d2bcb34"
(2) script exists
? 此命令用于判斷sha1是否已經(jīng)加載到Redis內(nèi)存中
# 代碼示例
> script exists 9295ec8b57e8e4d0a3e89f6cb3ab5b225d2bcb34
1) "1"
因為這里的參數(shù)可以填多個,返回值為被加載到Redis內(nèi)存中的Lua腳本個數(shù)。
(3) script flush
? 此命令用于清除Redis內(nèi)存已經(jīng)加載的所有Lua腳本。
# 代碼示例
> script flush
"OK"
> script exists 9295ec8b57e8e4d0a3e89f6cb3ab5b225d2bcb34
1) "0"
(4) script kill
此命令用于殺掉正在執(zhí)行的Lua腳本。如果Lua腳本比較耗時,甚至Lua腳本存在問題,那么此時Lua腳本的執(zhí)行會阻塞Redis,直到腳本執(zhí)行完畢或者外部進行干預(yù)將其結(jié)束。
柚子快報邀請碼778899分享:redis初級——Lua腳本
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。