1.redis的事务
Redis 通过 MULTI、EXEC、WATCH 等命令来实现事务(transaction)功能。
eg:
事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求。
在传统的关系式数据库中,常常用 ACID 性质来检验事务功能的可靠性和安全性。而redis中的事务不是原子性的(伪事务),一条失败,其余的会执行下去;
执行失败的原因有这些:
1.命令错误:比如说语法错误, set写成了sett,整个的事务将无法执行;
2.运行时错误:语法正确,例如参数错误,expire命令的第二个参数为时间,应该是Integer类型,结果写成了String类型;
3.业务逻辑错误:比如说应该用sadd,却误写成了zadd,从语法上讲,是没有毛病的,但是上面的写对的命令,已经执行入库了, 这种情况就需要开发人员自己修复了;
既然redis中的事务不是原子性的,那么如果要实现对redis的多命令事务操作,可以使用lua脚本;
2. LUA脚本
lua脚本命令的语法如下:
EVAL script numkeys key [key ...] arg [arg ...]
说明:
EVAL 为命令的前缀,固定写法;
script 为脚本语句,一段字符串,如"return xx,redis.call(xx);"
numkeys 指定后续参数有几个key;
key [key ...],是要操作的键,可以指定多个,在lua脚本中通过KEYS[1], KEYS[2]获取;
arg [arg ...],参数,在lua脚本中通过ARGV[1], ARGV[2]获取。
示例:
1. 将脚本直接写在命令中;
启动redis,打开redis-cli.exe
2. 通过.lua文件执行脚本
3. 工程中使用Lua脚本的示例
(1)lua脚本内容
(2)导入.lua文件
(3)代码中通过RedisClusterTemplete执行脚本
(4)调用的底层源码:
4. Lua脚本与multi命令的比较
Lua脚本与multi命令在执行过程中,遇到异常时的处理是不同的,这里附上几个关于multi命令与lua脚本的执行错误的情况下的结果测试:
测试(1)——存在语法错误:
发现问题,这里是在multi命令里面有语句错误,执行exec命令时会报错,导致错误语句的前后的其他正确的语句没有执行;
测试(2)——语法正确 执行时的参数转换错误:
语句未出错,而是由于方法的调用出错(例如参数类型错误,应该传入Integer而非String,而非语句错误),后续的命令依旧会执行;
测试(3)——Lua脚本中命令语法出错:(lua脚本)
再看看如果是语句本身出错了呢,即命令错误:
lua脚本如下:3个key,3个参数argv,分别set、分配ttl、set
执行lua脚本:
来看看执行结果:
测试(4)——Lua脚本执行时参数出错:(lua脚本)
lua脚本如下:3个key,3个参数argv,分别set、分配ttl、set
执行脚本:此时redis没有key为空,先让参数ARGV[2]为非Integer的值(参数错误)
此时来看看其他的语句的执行情况,看看后面的key3设置成功没有:
结果与测试(3)一样;
说明:lua脚本与multi命令(事务)是不一样的,如果发生错误(无论是语法还是参数错误),前面正确的语句可以被执行,而出现错误语句的后面的语句不会执行!
lua脚本的好处:
1.lua脚本是作为一个整体执行的.所以中间不会被其他命令插入;
2.可以把多条命令一次性打包,所以可以有效减少网络开销;
3.lua脚本可以常驻在redis内存中,所以在使用的时候,可以直接拿来复用.也减少了代码量.
小结:
单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。
对于multi命令的事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
对于lua脚本,如果在某一句执行失败,后面的指令是不会执行的。