目录
"事务"
介绍
使用事务,成功提交
使用事务,成功回滚
使用事务,语法错误,成功触发回滚
使用事务,执行错误,不会触发回滚
LUA脚本
介绍
使用lua脚本的好处
应用
例1:记录IP登录次数
例2:当10秒内请求3次后拒绝访问
lua脚本缓存
大家都知道在RDBMS中有事务操作,同样在Redis中也是支持"事务"的,只是redis支持的是弱事务性,跟我们平时理解上有些不太一样,下面来看看有哪些不一样。同时我们也可以通过lua脚本实现redis的事务操作。
Redis系列文章:
Redis系列(一)、CentOS7下安装Redis6.0.3稳定版
Redis系列(二)、数据类型之字符串String
Redis系列(三)、数据类型之哈希Hash
Redis系列(四)、数据类型之列表List
Redis系列(五)、数据类型之无序集合Set
Redis系列(六)、数据类型之有序集合ZSet(sorted_set)
Redis系列(七)、常用key命令
Redis系列(八)、常用服务器命令
redis提供简单的事务命令,由multi和exec组成,实际上相当于将多个命令添加到一个执行的集合内,multi为begin,exec为commit,discard相当于rollback,watch相当于锁,对象若在事务执行前被修改则事务被打断。
因此redis的事务机制为乐观锁,如果在高并发场景下,如果多个客户端同时对一个key进行了watch,只要有一个客户端提交成功,其他客户端的操作都是无效的,因此redis事务不适合在高并发场景下使用,使用lua脚本可以更好的解决事务解决不了的场景。
Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:
一个事务从开始到执行会经历以下三个阶段:
如果事务中出现错误:
#begin 开启一个redis事务
multi
#commit 提交事务里的命令队列
exec
#rollback 回滚
discard
#锁,监视一个或多个key,被监视的key若在提交事务前被修改,则事务被打断
watch key [key ...
#取消监视
unwatch
Redis2.6之后新增的功能,我们可以在redis中通过lua脚本操作redis。与事务不同的是事务是将多个命令添加到一个执行的集合,执行的时候仍然是多个命令,会受到其他客户端的影响,而脚本会将多个命令和操作当成一个命令在redis中执行,也就是说该脚本在执行的过程中,不会被任何其他脚本或命令打断干扰。正是因此这种原子性,lua脚本才可以代替multi和exec的事务功能。同时也是因此,在lua脚本中不宜进行过大的开销操作,避免影响后续的其他请求的正常执行。
redis脚本使用eval命令执行lua脚本,其中numkeys表示lua script里有多少个key参数,redis脚本根据该数字从后面的key和arg中取前n个作为key参数,之后的都作为arg参数:
eval script numkeys key [key ...] arg [arg ...]
#利用hash记录所有登录的IP次数
#key参数的数量必须和numkey一致,使用key或者argv可以实现一样的效果。如下面第一个命令里用了三个key,代表后面的三个参数分别对应脚本里的key1 key2 key3.第二个命令里用了一个key,代表了后面第一个参数对应脚本里的key1,后面第二和第三个参数对应脚本里的argv1和argv2
eval "return redis.call('hincrby', KEYS[1], KEYS[2], KEYS[3])" 3 h_host host_192.168.145.1 1
eval "return redis.call('hincrby', KEYS[1], ARGV[1], ARGV[2])" 1 h_host host_192.168.145.1 1
#1.给访问ip的key递增,2.判断该访问次数若为首次登录则设置过期时间10秒,3.若不是首次登录则判断是否大于3次,若大于则返回0,否则返回1。
eval "local request_times = redis.call('incr',KEYS[1]);if request_times == 1 then redis.call('expire',KEYS[1], ARGV[1]) end;if request_times > tonumber(ARGV[2]) then return 0 end return 1;" 1 test_127.0.0.1 10 3
通过上面的例子也可以看出,我们可以在redis里使用eval命令调用lua脚本,且该脚本在redis里作为单条命令去执行不会受到其余命令的影响,非常适用于高并发场景下的事务处理。同样我们可以在lua脚本里实现任何想要实现的功能,迭代,循环,判断,赋值 都是可以的。
redis脚本也支持将脚本进行持久化,这样的话,下次再使用就不用输入那么长的lua脚本了。事实上使用eval执行的时候也会缓存,eval与load不同的是eval会将lua脚本执行并缓存,而load只会将脚本缓存。相同点是它们都使用sha算法进行缓存,因此只要lua脚本内容相同,eval与load缓存的sha码就是一样的。而缓存后的脚本,我们可以使用evalsha命令直接调用,极大的简化了我们的代码量,不用重复的将lua脚本写出来。
#eval 执行脚本并缓存
eval script numkeys key [key ...] arg [arg ...]
#load 缓存lua脚本
SCRIPT LOAD script
#使用缓存的脚本sha码调用脚本
EVALSHA sha1 numkeys key [key ...] arg [arg ...]
#使用sha码判断脚本是否已缓存
SCRIPT EXISTS sha1 [sha1 ...]
#清空所有缓存的脚本
SCRIPT FLUSH
#杀死当前正在执行的所有lua脚本
SCRIPT KILL
希望本文对你有帮助,请点个赞鼓励一下作者吧~ 谢谢!