Redis之事务与Lua
1. 为什么使用事务与lua
为了保证多条命令组合的原子性
2. Redis事务
1. redis事务功能
将一组需要一起执行的命令放到multi和exec两个命令之间
multi代表事务开始
exec代表事务结束
他们之间的命令是原子顺序执行的
discard代表中止事务
例子:
127.0.0.1:6379> multi OK 127.0.0.1:6379> set name yangjianbo QUEUED 127.0.0.1:6379> exec 1) OK 在事务还没有提交前,其它客户端查看get name, 为空,直到提交以后,才显示对应的value。
2. 如果事务中的命令出现错误,Redis的处理机制也不尽相同
1. 命令输入错误(语法错误)
将命令写错
例子:
127.0.0.1:6379> get b "2" 127.0.0.1:6379> multi OK 127.0.0.1:6379> incr b QUEUED 127.0.0.1:6379> sett c (error) ERR unknown command 'sett' 127.0.0.1:6379> exec (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> get b "2" 当命令出现语法错误的时候,提交事务的时候会有报错。事务内其他的命令执行也不会生效。
2. 运行时错误
将命令用错了,但不是语法错误,本来应该使用set,使用了mset
例子:
127.0.0.1:6379> get b "2" 127.0.0.1:6379> multi OK 127.0.0.1:6379> incr b QUEUED 127.0.0.1:6379> mset c 1 QUEUED 127.0.0.1:6379> exec 1) (integer) 3 2) OK 127.0.0.1:6379> get b "3" 将命令因为人为因素写错的例如:sadd写成了zadd ,redis认为使用没有问题,redis不支持回滚。
3. redis的watch命令
1. 事务中watch命令演示时序

当一些场景在事务执行之前,确保事务中的key没有被其它客户端修改过,才能执行事务,否则不执行。Redis提供了watch命令来解决这个问题。
2. 例子:
客户端1 127.0.0.1:6379> get name "yangjianbo" 127.0.0.1:6379> watch name OK 127.0.0.1:6379> multi OK 客户端2 127.0.0.1:6379> append name yichangkun (integer) 20 客户端1 127.0.0.1:6379> append name luoying QUEUED 127.0.0.1:6379> exec (nil) 127.0.0.1:6379> get name "yangjianboyichangkun" 因为客户端2在exec之前修改了name值,造成客户端1事务exec结果为nil。
3. Lua用法简述
1. 数据类型
| 数据类型 | 描述 |
|---|---|
| nil | 这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)。 |
| boolean | 包含两个值:false和true。 |
| number | 表示双精度类型的实浮点数 |
| string | 字符串由一对双引号或单引号来表示 |
| function | 由 C 或 Lua 编写的函数 |
| userdata | 表示任意存储在变量中的C数据结构 |
| thread | 表示执行的独立线路,用于执行协同程序 |
| table | Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。 |
1. 字符串
[root@192 ~]# lua -i Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio > local string a='yangjianbo' > print(a) yangjianbo
2. 数组
> local tables yunwei={"yangjianbo","yichangkun","luoying"}
> print (yunwei[3])
luoying
2. 逻辑处理
1. for
1. 计算1到100的和
local int sum = 0 for i = 1, 100 do sum = sum + i end -- 输出结果为5050 print(sum)
2. 遍历某个数组
> for i=1,#yunwei >> do >> print(yunwei[i]) >> end yangjianbo yichangkun luoying
3. 通过内置函数ipairs,遍历出所有的索引下标和值
> for index,value in ipairs(yunwei) >> do >> print(index) >> print(value) >> end 1 yangjianbo 2 yichangkun 3 luoying
2. while
local int sum = 0 local int i = 0 while i <= 100 do sum = sum +i i = i + 1 end --输出结果为5050 print(sum)
3. if else
local tables myArray = {"redis", "jedis", true, 88.0}
for i = 1, #myArray
do
if myArray[i] == "jedis"
then
print("true")
break
else
--do nothing
end
end
4. 哈希
local tables user_1 = {age = 28, name = "tome"}
--user_1 age is 28
print("user_1 age is " .. user_1["age"])
遍历user_1,使用Lua的内置函数pairs
> for key,value in pairs(user_1)
>> do print(key .. value)
>> end
age28
nametome
5. 函数
function funcName()
...
end
contact函数将两个字符串拼接:
function contact(str1, str2)
return str1 .. str2
end
--"hello world"
print(contact("hello ", "world"))
4. Redis与Lua
1. 在Redis中使用Lua
1. eval
1. 语法
eval 脚本内容 key个数 key列表 参数列表
2. 例子
192.168.1.201:6379> eval "return {KEYS[1],KEYS[2],KEYS[3],ARGV[1],ARGV[2],ARGV[3]}" 3 redis world name yang age 18
1) "redis"
2) "world"
3) "name"
4) "yang"
5) "age"
6) "18"
3. 执行过程

2. redis-cli --eval
与eval本质相同
3. evalsha
首先将Lua脚本加载到Redis服务端,得到该脚本的SHA1校验和,evalsha命令使用SHA1作为参数可以直接执行对应Lua脚本,避免每次发送Lua脚本的开销。这样客户端不需要每次执行脚本内容,而脚本也会常驻在服务端,脚本功能得到了复用。

1. 加载脚本
redis-cli script load "$(cat 1.lua)"
1.lua的内容:print("hello") 执行加载的时候,最好要进入到lua脚本所在的目录中
2. 执行脚本
EVALSHA a5e636d1097ffc88e9ed41027511f1fbe3fe2efd 1 yangjianbo name
2. Lua的Redis API
Lua可以使用redis.call函数实现对redis的访问
redis.call("set", "hello", "world")
redis.call("get", "hello")
放在Redis的执行效果如下:
127.0.0.1:6379> eval 'return redis.call("get", KEYS[1])' 1 hello
"world
3. Lua脚本带来的好处
1. Lua脚本在redis中是原子执行的,执行过程中不会插入其他命令
2. Lua脚本可以实现复用的效果
3. Lua脚本可以将多条命令一次性打包,有小弟减少网络开销
4. Redis如何管理lua脚本
1. 加载脚本
script load
2. 判断sha1是否已经加载到redis内存中
script exists a5e636d1097ffc88e9ed41027511f1fbe3fe2efd
3. 清除redis内存已经加载的所有lua脚本
script flush
4. 杀掉lua脚本
script kill
当redis发生阻塞时,会有提示:需要执行script kill命令
当redis发生写操作时,那么script kill将不会生效

浙公网安备 33010602011771号