Install
macOS
$ brew install lua
Demo
$ lua -i
Lua 5.4.3 Copyright (C) 1994-2021 Lua.org, PUC-Rio
>
Variables
--- 全局变量
name = 'aaa'
--- 局部变量
local age = 18
-- Variable definition:
local a, b
-- Initialization
a = 10
b = 30
print("value of a:", a)
print("value of b:", b)
-----
local d , f = 5 ,10 --declaration of d and f as local variables.
d , f = 5, 10; --declaration of d and f as global variables.
d, f = 10 --[[declaration of d and f as global variables.
Here value of f is nil --]]
数据类型
nil
空boolean
布尔值number
数字string
字符串table
表
boolean
number
table 类型
它既是数组,又是 map。
数组不分具体类型,演示如下
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio
> arr_table = {'aa','bb',1}
> print(arr_table[1])
aa
> print(arr_table[3])
1
> print(#arr_table)
3
作为字典:
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio
> arr_table = {name = 'aa', age = 18}
> print(arr_table['name'])
aa
> print(arr_table.name)
aa
> print(arr_table[1])
nil
> print(arr_table['age'])
18
> print(#arr_table)
0
或者
a = {} -- create a table and store its reference in `a'
k = "x"
a[k] = 10 -- new entry, with key="x" and value=10
a[20] = "great" -- new entry, with key=20 and value="great"
print(a["x"]) --> 10
k = 20
print(a[k]) --> "great"
a["x"] = a["x"] + 1 -- increments entry "x"
print(a["x"]) --> 11
混合模式:
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio
> arr_table = {'aa','Felordcn',1,age = 18,nil}
> print(arr_table[1])
aa
> print(arr_table[4])
nil
> print(arr_table['age'])
18
> print(#arr_table)
3
Ref
- https://www.lua.org/pil/2.5.html
- https://www.lua.org/pil/11.1.html
- https://www.tutorialspoint.com/lua/lua_arrays.htm
Insert and Remove
local a = {}
table.insert(a, "aaa")
table.insert(a, "bbb")
return a
-- output:
1) "aaa"
2) "bbb"
https://www.lua.org/pil/19.2.html
Get Len
local a = {}
table.insert(a, "aaa")
table.insert(a, "bbb")
table.getn(a)
String
Lua denotes the string concatenation operator by “..
” (two dots). If any of its operands is a number, Lua converts that number to a string.
print("Hello " .. "World") --> Hello World
print(0 .. 1) --> 01
Remember that strings in Lua are immutable values. The concatenation operator always creates a new string, without any modification to its operands:
a = "Hello"
print(a .. " World") --> Hello World
print(a) --> Hello
Ref
Flows
If
if op == "+" then
r = a + b
elseif op == "-" then
r = a - b
elseif op == "*" then
r = a * b
elseif op == "/" then
r = a / b
else
error("invalid operation")
end
For
local arr = { 1, 2, name = 'aaa' }
for i, v in ipairs(arr) do
print('i = ' .. i)
print('v = ' .. v)
end
print('-------------------')
for i, v in pairs(arr) do
print('p i = ' .. i)
print('p v = ' .. v)
end
-- output:
i = 1
v = 1
i = 2
v = 2
-----------------------
p i = 1
p v = 1
p i = 2
p v = 2
p i = name
p v = aaa
Lua in Redis
EVAL命令
Redis中使用EVAL
命令来直接执行指定的Lua脚本。
EVAL luascript numkeys key [key ...] arg [arg ...]
Lua脚本中包括两组参数:KEYS[]和ARGV[],两个数组下标从1开始。一个值得去遵守的最佳实践是:把redis操作所需的key通过KEYS进行参数传递,其他的Lua脚本所需的参数通过ARGV进行传递。
EVAL
命令的关键字。luascript
Lua 脚本。numkeys
指定的Lua脚本需要处理键的数量,其实就是key
数组的长度。key
传递给Lua脚本零到多个键,空格隔开,在Lua 脚本中通过KEYS[INDEX]
来获取对应的值,其中1 <= INDEX <= numkeys
。arg
是传递给脚本的零到多个附加参数,空格隔开,在Lua脚本中通过ARGV[INDEX]
来获取对应的值,其中1 <= INDEX <= numkeys
。
Lua脚本通过各种语言的redis客户端都可以调用,我们就简单一点使用redis-cli
看下面的redis命令行:
eval "redis.call('set', KEYS[1], ARGV[1])" 1 key:name value
Demo
# case 1
127.0.0.1:6379> EVAL "return 'hello world'" 0
"hello world"
# case 2
127.0.0.1:6379> eval 'redis.call("hget","foo","bar")' 0
(error) ERR Error running script (call to f_9e6d82f0740926e0a70775430bda59a54d4e0664): ERR Operation against a key holding the wrong kind of value
127.0.0.1:6379> eval 'redis.pcall("hget","foo","bar")' 0
(nil)
# case 3
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> get hello
"world"
127.0.0.1:6379> EVAL "return redis.call('GET',KEYS[1])" 1 hello
"world"
127.0.0.1:6379> EVAL "return redis.call('GET','hello')"
(error) ERR wrong number of arguments for 'eval' command
127.0.0.1:6379> EVAL "return redis.call('GET','hello')" 0
"world"
# If not return, we get nothing even if such a key exists
127.0.0.1:6379> EVAL "redis.call('GET','hello')" 0
(nil)
# case 4
# 设置hkeys为键Hash值
hmset hkeys key:1 value:1 key:2 value:2 key:3 value:3 key:4 value:4 key:5 value:5 key:6 value:6
# 建一个order为键的集合,并给出顺序
zadd order 1 key:3 2 key:1 3 key:2
eval "local order = redis.call('zrange', KEYS[1], 0, -1); return redis.call('hmget',KEYS[2],unpack(order));" 2 order hkeys
call函数和pcall函数
在上面的例子中我们通过redis.call()
来执行了一个SET
命令,其实我们也可以替换为redis.pcall()
。它们唯一的区别就在于处理错误的方式,前者执行命令错误时会向调用者直接返回一个错误;而后者则会将错误包装为一个我们上面讲的table
:
127.0.0.1:6379> EVAL "return redis.call('no_command')" 0
(error) ERR Error running script (call to f_1e6efd00ab50dd564a9f13e5775e27b966c2141e): @user_script:1: @user_script: 1: Unknown Redis command called from Lua script
127.0.0.1:6379> EVAL "return redis.pcall('no_command')" 0
(error) @user_script: 1: Unknown Redis command called from Lua script
这就像Java遇到一个异常,前者会直接抛出一个异常;后者会把异常处理成JSON返回。
值转换
由于在Redis中存在Redis和Lua两种不同的运行环境,在Redis和Lua互相传递数据时必然发生对应的转换操作,这种转换操作是我们在实践中不能忽略的。例如如果Lua脚本向Redis返回小数,那么会损失小数精度;如果转换为字符串则是安全的。
127.0.0.1:6379> EVAL "return 3.14" 0
(integer) 3
127.0.0.1:6379> EVAL "return tostring(3.14)" 0
"3.14"
脚本管理
SCRIPT LOAD
预加载脚本到Redis以达到重复使用的效果,从而可以避免多次加载脚本导致的浪费带宽。
script load "return redis.call('get', KEYS[1])"
预加载完成之后,你会看到下面的一段输出
“4e6d8fc8bb01276962cce5371fa795a7763657ae”
这是一个具有唯一性的hash字符串,这个hash就代表着我们刚刚预加载的Lua脚本,我们可以通过EVALSHA命令执行该脚本。如:
evalsha 4e6d8fc8bb01276962cce5371fa795a7763657ae 1 key:name
执行的结果与下面的是一致的。
eval "return redis.call('get', KEYS[1])" 1 key:name
SCRIPT FLUSH
既然有缓存就有清除缓存,但是遗憾的是并没有根据SHA来删除脚本缓存,而是清除所有的脚本缓存,所以在生产中一般不会再生产过程中使用该命令。
SCRIPT EXISTS
以SHA标识为参数检查一个或者多个缓存是否存在。
127.0.0.1:6379> SCRIPT EXISTS 1b936e3fe509bcbc9cd0664897bbe8fd0cac101b 1b936e3fe509bcbc9cd0664897bbe8fd0cac1012
1) (integer) 1
2) (integer) 0
SCRIPT KILL
终止正在执行的脚本。但是为了数据的完整性此命令并不能保证一定能终止成功。如果当一个脚本执行了一部分写的逻辑而需要被终止时,该命令是不凑效的。需要执行SHUTDOWN nosave
在不对数据执行持久化的情况下终止服务器来完成终止脚本。
Debug
参数 –eval script key1 key2 , arg1 age2 这种模式,key和value用一个逗号隔开就好了,
[root@localhost Desktop]# redis-cli --eval /usr/redis/sbin/1.lua username age , jack 20
1) "username"
2) "age"
3) "jack"
4) "20"
[root@localhost Desktop]#
Reference
- https://redis.io/commands/eval
- https://segmentfault.com/a/1190000039287716
- https://segmentfault.com/a/1190000037518418