0%

Redis事务与Lua

事务

Redis提供了简单的事务功能以及Lua脚本,来保障多个命令组合的原子性,也就是ACID中的A(Atomictiy)。Redis事务由multi命令显示开启,并以exec结束事务。Redis事务不支持回滚特性,使用过程中需要多注意。

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> multi
OK
127.0.0.1:6379> sadd user lu
QUEUED
127.0.0.1:6379> sadd id 1
QUEUED
127.0.0.1:6379> exec
1) (integer) 1
2) (integer) 1

sadd返回的结果为QUEUED,表示该命令并没有真正的执行,而是暂时保存在redis中,如果要停止事务执行,可以在执行exec命令前执行discard命令。

针对Redis事务过程中出现错误的处理机制也有区别。

  • 命令错误:当出现语法错误,会造成整个事务无法执行
  • 运行时错误:当出现把sadd错误写成zadd命令引起的错误时,Redis不支持回滚操作,执行成功的需要人工干预

某些事务场景下,需要确保事务中的key没有被其它客户端修改过,否则将不执行事务。Redis提供了watch命令来解决该问题,watch命令执行在multi命令之前

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#SESSION A
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> watch key
OK
127.0.0.1:6379> multi
OK
#SESSION B
127.0.0.1:6379> append hello java
(integer) 9
#SESSION A
127.0.0.1:6379> append hello python
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get hello
"worldjava"

Lua

初识Lua

Lua脚本使开发者通过修改源码来定制属于自己的Redis命令,作为Redis的一个扩展。Lua脚本提供了几种数据类型:booleans、numbers、strings、tables。

定义一个strings类型的数据,其中local定义val是一个局部变量,如果没有指定local就是全局变量

1
local strings val = "hello"

在Lua中如果要使用数组功能,可以使用tables类型,不同的是tables的数组下标从1开始

1
local tables array= {'mysql','redis','oracle'}

如果想遍历整个数组,可以使用for或者while,这和很多开发语言中的循环结果一致
(1)FOR循环

1
2
3
4
for i=1,#array
do
print(array[i])
end

其中#array能够获取数组的长度

除此之外,Lua还提供了内置函数ipairs,使用for index,value ipairs(tables)可以遍历出所有的索引下标和值

1
2
3
4
5
for index,value in ipairs(array)
do
print(index)
print(value)
end

(2)WHILE循环

1
2
3
4
5
6
7
8
local int sum = 0
local int i = 0
while i <= 100
do
sum = sum +i
i = i + 1
end
print(sum)

(3)IF-ELSE

1
2
3
4
5
6
7
8
9
10
11
local tables array = {'mysql','redis','oracle'}
for i=1,#array
do
if array[i] == 'mongodb'
then
print('true')
break
else
print('false')
end
end

(4)哈希

1
2
local tables user = {name="lu",age=25}
print("user age is " .. user["age"])

在Lua中函数以function开头,end结尾。

1
2
3
4
function contact(str1,str2)
return str1 .. str2
end
print(contact("hello","world"))

执行Lua脚本

在redis中执行lua脚本有两种方法:eval或者evalsha。

eval

1
2
3
#eval 脚本内容 key个数 key 列表 参数列表
127.0.0.1:6379> eval 'return "hello " ..KEYS[1] .. ARGV[1]' 1 redis server
"hello redisserver"

如果脚本太长,也可以通过redis-cli –eval执行执行Lua脚本文件

evalsha
除了eval,redis也提供了evalsha来执行Lua。首先将Lua加载到Redis服务端,得到该脚本的SHA1校验和,evalsha使用SHA1作为参数可以直接执行对应的Lua脚本,避免每次都发送Lua脚本。

加载脚本:script load命令可以将脚本内容到Redis内存中

1
redis-cli script load "$(cat test.lua)"

执行脚本:

1
#evalsha sha1 key个数 key列表 参数列表

Redis API
Lua可以使用redis.call函数实现对Redis的访问,可以直接调用Redis命令

1
redis.call("set","hello","world")

除了redis.call外Redis还提供了Redis.pcall,不同之处在于redis.call执行失败,将结束脚本返回报错,而pcall会忽略错误继续执行脚本。

管理Lua脚本

Redis提供了4个命令实现对Lua脚本的管理

  • script load:将Lua脚本加载到Redis内存中
  • scripts exists:根据提供的SHA1值判断脚本是否已经加载到Redis内存中
  • script flush:清除所有Redis内存中的Lua脚本
  • scripts kill:用于杀掉正在执行的Lua脚本,但脚本执行了写操作kill将不会生效