redis学习第五章
脚本
- 介绍
redis在2.6版本中退出了脚本功能,允许开发者使用Lua语言编写脚本传到redis中执行。在Lua脚本中可以调用大部分的redis命令。使用脚本的好处:
1).减少网络开销。
2).原子操作:redis会将整个脚本作为一个整体执行,也就是说不必单行出现静态条件,也不用使用事务,事务可以完成的,脚本也可以完成。
3).复用:客户端发送的脚本会永远存储在redis中,这意味着其他客户端(可以是其他语言开发的项目)可以复用这一脚本而不需要使用代码完成同样的逻辑。
- 实例:访问频率控制
因为无需考虑事务,使用redis脚本实现访问频率限制就非常简单了。Lua如下:
local times = redis.call('incr',KEYS[1])
if times == 1 then
-- KEYS[1] 键刚创建,所以为其设置生存时间
redis.call('expire',KEYS[1],ARGV[1])
end
if times > tonumber(ARGV[2]) then
return 0
end
return 1
如何运行测试该脚本呢?
首先把这段代码村委ratelimiting.lua,然后在命令行中输入:
$redis-cli --eval /path/to/ratelimiting.lua rate.limiting:127.0.0.1 , 10 3
其中–eval是告诉redis-cli读取并运行后面的Lua脚本
/path/to/ratelimiting.lua是lua脚本的位置
rate.limiting:127.0.0.1 , 10 3 是lua脚本的参数,其中“,”之前的是键,之后的是参数。分别用KEYS[1]和ARGV[1]、ARGV[2]获取
结合脚本可知,改脚本是限制访问频率为10s内三次。
注意: 参数和键之间的逗号“,”两边的空格不能省略,否则会出错。
- Lua语言
3.1).Lua语法
3.1.1).数据类型
Lua是一个动态类型的语言,一个变量可以存储任何类型的值。编写redis脚本会用到的类型如下表(Lua常用数据类型):
类型名 | 取值 |
---|---|
空(nil) | 空类型只包含一个值,即nil。nil表示空,所以没有赋值的变量或表的字段都是nil |
布尔(boolean) | 布尔类型包含true和false |
数字(number) | 整数和浮点都是使用数字类型来存储,如:1、0.2、3.5e20等 |
字符串(string) | 该类型可以存储字符串,且与redis的键值一样都是二进制安全的。字符串可以使用单引号或双引号表示,两个字符是相同的。比如’a’,“b” 都是可以的。字符串中可以包含转义字,如\n、\r等 |
表(table) | 表类型是lua语言中唯一的数据结构,既可以当数组又可以当字典(表类型的索引从1开始) |
函数(function) | 函数在Lua中是一等值(first-class value),可以存储在变量中、作为函数的参数或返回结果 |
3.1.2).变量
Lua的变量分为全局变量和局部变量。全局变量无需声明就可以直接使用,默认值是nil。如:
a = 1 – 为全局变量a赋值
print(b) --无需声明直接使用,默认值为nil
a = nil – 删除全局变量a的方法是将其赋值为nil。全局变量真没有声明和未声明之分,只有非nil和nil的区别。
在redis脚本中不能使用全局变量,值允许使用局部变量,以防止脚本之间相互影响。声明全局变量的方法为local变量名,就像这样:
local c --声明一个局部变量c,默认值是nil
local d = 1 – 声明一个局部变量d并赋值为1
local e, f --可以同时声明多个局部变量
声明一个存储函数的局部变量的方法为:
local say_hi = function ()
print ‘hi’
end
注意: 变量名只能是非数字开头,只能包含字母、数字和下划线,区分大小写。变量名不能与Lua关键字相同,保留关键字如下:
and break do else elseif
end false for function if
in local nil not or
repeat return then true until while
局部变量的作用域为从声明开始到所在层的语句块末尾,比如:
local x = 10
if true then
local x = x + 1
print(x)//11
do
local x = x + 1
print(x)//12
end
print(x)//11
end
print(x)//10
3.1.3).注释
Lua的注释分为单行和多行两种:
单行注释以–开始,到行位结束,一般习惯在–后面跟一个空格
多行注释以–[[开始,到]]结束,如:
–[[
这是一个注释
]]
3.1.4).赋值
Lua支持多重赋值:
local a,b = 1,2 --a=1 b=2
local c,d = 1,2,3 – c=1 d=2 3废弃
local e,f = 1 – e=1 f = nil
在执行多重赋值时,Lua会先计算所有表达式的值,比如:
local a = {1,2,3}
local i = 1
i,a[i] = i + 1 , 5
Lua计算所有表达式的值后,上面最后一个赋值语句变为i,a[1] = 2,5,所以赋值后i的值为2,a则为{5,2,3}
Lua中函数也会返回多个值。
3.1.5).操作符
Lua有以下5类操作符。
-
数学操作符。数学操作符包括常见的+、-、*、/、%(取模)、-(一元操作符,取负)和幂运算^。
数学操作符的操作数如果是字符串会自动转换成数字,比如:
print(‘1’ + 1) – 2
print(‘10’ * 2) – 20 -
比较操作符
== 比较两个操作数的类型和值是否都相等
= 与==的结果相反
<,>,<=,>= 小于,大于、小于等于、大于等于
比较操作符的结果一定是布尔类型,比较操作符不同于数学操作符,不会对两边的操作数进行自动类型转换,也就是说:
print(1 == ‘1’) --false,二者类型不同,不会进行自动类型转换
print({‘a’} == {‘a’}) --false,对于表类型的值,比较的是二者的引用
如果比较字符串和数字,可以手动进行类型转换,如下:
print(1 == tonumber(‘1’)) – true
print(‘1’ == tostring(1)) – true
其中 tonumber函数还可以进行进制转换,比如 print(tonumber(‘F’,16)) – 将字符串’F’从16进制转换成10进制结果是15 -
逻辑操作符
not 根据操作数的真和假相应地返回false和true
and a and b 中如果a是真则返回b,否则返回a
or a or b 中如果a是真则返回a,否则返回b
只要操作数不是nil或false,逻辑操作符就认为操作数是真,否则是假。特别需要注意的是即时是0或空字符串也被当做真。例如:
print(1 and 5) – 5
print(1 or 5) – 1
print(not 0) – false
print(’’ or 1) – ‘’