一、前言:
redis 为我们提供了很多的命令,但是很多时候我们需要组合这些命令,但是有设计到一个问题:命令组合起来之后,在高并发的情况下,怎么保证原子性呢?此时lua为我们提供了解决的方案。lua 还是比较好入门的,但是我今天在玩这个玩意儿的时候还是遇到了很多问题,因为分享出来希望大家不会遇到这个问题。
二、还是言明环境:
- redis 3.2.100 for windows
因为最近 windows10 for docker 的网络没有整明白不得已在windows安装了redis,这种东西最好是在linux下面玩
三、lua 与 reids 的简单入门:
这里只是做一个简单的介绍,因为在这里详细的介绍一门语言不现实,还是以使用为主,
3.1 实现一个简单的需求:
- 获取以天为自增的唯一序列
- 这个里面首先需要自增,肯定是要使用到 incr 这个命令的
- 其实需要以天为单位,说明这个序列是需要有过期时间的
- 需要批量的生成,因为是进行批量的请求,但是总不能每一条记录都请求一次这个太不划算,io交互太多
ps:在新版的 RedisTemplate 里面提供了这个方法,自增的同时是可以设置过期时间的,但是项目中配置的是 jedis,所以很无奈的只能自己造。这个通过事务也能实现,但是没有必要,lua可以是一个更好的选择
3.2 lua 的简单入门:
- 接受外面传入的参数key
local key = KYES[1]
local 表明这个是局部变量,建议能用局部变量就用局部变量 - 接受值,例如过期时间或者key对应的值
local tll = ARGV[1] - 在 lua 中的一些结构,对于if for 这样的语句块需要使用 end 来作为结尾,例如:
if true then
todo
end
for i=0,len do
todo
end
- lua 中的注释是以 -- 来标示的
- lua 中的调用redis 命令:redis.call('commond',key,val……)
- lua 中字符串拼接使用 .. 即可拼接
3.3 开始实战:
新建文件以 .lua 结尾
-- 接受key
local key = KEYS[1]
-- 接受过期时间
local ttl = ARGV[1]
-- 需要生成的个数
local len = ARGV[2]
-- 定义一个接受的数组,来接受批量生成的序列号
local codeArr = {}
-- 判断,如果key不存在,则先初始化key,且设置过期时间
if redis.call('EXISTS',key) == 0 then
-- 插入一条记录
redis.call('SET',key,0)
-- 设置过期时间
redis.call('EXPIRE',key,ttl)
end
for i = 0 , len do
-- 循环产生序列号,并放置到数组当中,
codeArr[i] = redis.call('INCR',key)
end
-- 返回给客户端
return codeArr
3.4 redis 客户端调用 lua 脚本
ps:注意下面演示的是在 windows 的环境下
- 进入到 redis 的安装的bin目录
- 在cmd命令窗口中执行下列命令:
redis-cli.exe --eval ExistAndIncre.lua RESERVE_CODE , 100000 10
需要注意的是:
- 我们不能进入到redis的客户端里面去了,要在外面的环境中执行,否则会报错
-
--eval lua文件位置 后面的第一个参数不需要知名有多少个key,否则这个就会作为 KEYS[1] 的值,目前不知道原因,测试出来的是这样的结果,这个和大多博客上的描述的不一样,切记
image.png
image.png - 中间的 ',' 注意前后都有一个空格,同时后面多个值的时候使用空格隔开即可
四、lua 的好处:
lua 主要是类似于批处理的功能,把众多的命令组合起来进行执行,但是这些在 lua 脚本中组合的命令是原子的,在高并发的情况下很多时候判断和执行如果不是原子的话,由于先后顺序的原因很有可能发生数据不一致的情况,所以我们要避免在判断和执行之间不能有时间窗口,lua就是为了解决这个问题的
很多复杂的场景中,需要组合众多的 redis 的命令的时候就可以考虑 lua ,在 redisson 的框架中的实现也是大量的采用了lua脚本的
由于工作需要来学习的,所以不是很深入,以后有机会再使用到在慢慢的补。
博客首发地址csdn:https://blog.csdn.net/weixin_42849915
简书地址:https://www.jianshu.com/u/4b48be4cf59f
希望结识更多的乐于分享的伙伴一起前行
网友评论