Spring Redis中使用Lua脚本实现高并发原子操作
发布日期:2021-06-29 21:56:32 浏览次数:2 分类:技术文章

本文共 2987 字,大约阅读时间需要 9 分钟。

1. 前言

在中我对 Lua 语言的一些简单的语法及其在 Redis 中的操作进行了介绍,但是在 Java 开发中我们还需要进一步的学习才能使这种技术落地。今天就结合Spring Data Redis这个我们经常使用的 Redis 开发组件来实际尝试一下 Lua 脚本。

2. Lua 实现抽奖

模拟一个抽奖场景,从奖池中进行随机抽奖。规则如下:

  • 中奖的人只能从奖池中抽取。

  • 每个人只能中奖一次。

  • 中奖总人数不能超过奖项的设置数。

  • 生成中奖名单。

规则有了,我们先来分析如何使用 Redis 实现。Redis 提供了 SET 集合,这种集合有点类似 Java 中的Set,放无重复的元素而且是无序的,可以满足随机性和奖池候选人的唯一性。同时它还提供了很多操作来满足抽奖的需要。接下来我们进行一一演示。

Redis SET 的一些操作。

基于篇幅我这里只演示一些抽奖可以用的上的 Redis 操作。

SET 添加元素。

添加一个到多个元素,使用SADD命令往lottery中添加多个元素来模拟往奖池中加人。

127.0.0.1:6379> sadd lottery u1 u2 u3 u4 u5 u6 u7(integer) 7127.0.0.1:6379> sadd lottery u1(integer) 0

如果没有lottey这个 key 就新建该 key,有就直接添加并返回成功添加的元素个数。同时你会发现如果集合中存在了添加的元素是无法被再次添加的。

查询集合中的元素

查询所有元素通过SMEMBERS命令。

127.0.0.1:6379> smembers lottery1) "u2"2) "u7"3) "u6"4) "u4"5) "u1"6) "u3"7) "u5"

随机抽取 N 个元素

SET 集合有两个命令都能满足随机抽取 N 个元素,分别是SPOPSRANDMEMBER,它们的区别在于SPOP会将选中的元素从原来的集合中剔除,而SRANDMEMBER不会。我们分别来使用这两个命令来随机从lottery中抽取 2 个元素来看看。

127.0.0.1:6379> srandmember lottery 21) "u2"2) "u4"127.0.0.1:6379> smembers lottery1) "u2"2) "u7"3) "u6"4) "u4"5) "u1"6) "u3"7) "u5"127.0.0.1:6379> spop lottery 21) "u3"2) "u5"127.0.0.1:6379> smembers lottery1) "u2"2) "u7"3) "u6"4) "u4"5) "u1"

lottery来说,如果你的奖池人数一次性添加的不再增加使用SPOP;如果动态添加,为了保证中奖的人不再次进入奖池应该使用SRANDMEMBER

抽奖脚本

接下来就是抽奖脚本,我们从lottery中抽出特定的人放入中奖名单,另外一个集合chosen中。

按道理 Redis 抽奖脚本在 Lua 中应该是这样的:

function draw(KEYS,ARGV)-- 抽奖逻辑 函数体end

但是我们只需要编写抽奖逻辑的函数体,然后把函数体写入.lua文件中,在 Maven 项目中放入META-INF/scripts文件夹中,如图所示:

Maven项目中约定lua脚本的文件位置

draw.lua的逻辑为:

--- 简单抽奖脚本  return 结果最终传递给Java 应用-- 奖池的keylocal lottery_key = KEYS[1]-- 中奖名单的keylocal chosen_key = KEYS[2]-- 预定抽奖的人数local lottery_count = ARGV[1]-- 如果预定抽奖的人数大于0才开始抽奖if tonumber(lottery_count) > 0 then    -- 奖池中抽奖 返回的是 被抽中的人组成的数组    local chosen_list = redis.call('SRANDMEMBER', lottery_key, lottery_count);    -- 将抽中的人添加到中奖名单中 返回中奖的人数    if chosen_list then        return redis.call('SADD', chosen_key, unpack(chosen_list))    else        return 0    endelse    return 0end

这里的逻辑仅仅为了演示用,实际上要根据你的业务进行编写,lua 相关的语法请参考。

3. 对应的 Java 代码

Spring Data Redis中的RedisTemplate提供了execute方法来执行 Lua 脚本,这里我选择使用下面的方法:

@Overridepublic 
 T execute(RedisScript
 script, List
 keys, Object... args) {   return scriptExecutor.execute(script, keys, args);}
  • RedisScript Redis 脚本的抽象,用来加载脚本。

  • keys对应 Lua 脚本中的 KEYS,用来传入 Redis 的 KEY,在 Lua 脚本中可以通过 KEYS[索引]来取值,例如取第一个值KEYS[1]

  • args用来向 Lua 脚本传递其它的参数,在 Lua 脚本中可以通过ARGV[索引]来取值。

我们利用draw.lua脚本从 Redis 的lottery集合中抽取5名幸运者并把他们添加到中奖名单chosen集合中:

RedisScript
 redisScript = RedisScript.of(new ClassPathResource("META-INF/scripts/draw.lua"), Long.class);Long chosenCount = stringRedisTemplate.execute(redisScript, Arrays.asList("lottery", "chosen"), Collections.singletonList("5"));

构造RedisScript对象时务必指定返回值对象以保证 Lua 脚本对象和 Java 的返回值能对应上,否则将出现异常。参见org.springframework.data.redis.connection.ReturnType枚举。

4. 总结

到此 Redis 利用 Lua 脚本进行抽奖的整套逻辑就完成了。Lua 脚本在 Redis 中通常是为了保证高并发下的原子性,当你考虑是否需要使用它时应该充分考虑你的业务和架构是否适合使用它,而非为了“炫技”。

好了今天的分享就到这里,我是:码农小胖哥 多多关注

更多干货分享关注下方公众号

往期推荐

深度内容

推荐加入

最近热门内容回顾   #技术人系列

转载地址:https://didispace-wx.blog.csdn.net/article/details/109268627 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:为何从单体架构迁移到微服务这么难?
下一篇:【1024】不整虚的,红包以备,致敬一线同僚!

发表评论

最新留言

网站不错 人气很旺了 加油
[***.192.178.218]2024年04月06日 16时45分18秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

【NLP学习笔记】NLP基础知识框架图 2019-04-30
【深度学习笔记】卷积的输入输出的通道、维度或尺寸变化过程 2019-04-30
【NLP学习笔记】训练集、验证集和测试集的概念及划分 2019-04-30
【NLP学习笔记】conda换源 2019-04-30
【深度学习笔记】常见的图像增强方法:scaling、rotating、flipping、random cropping 2019-04-30
【深度学习笔记】标准卷积 2019-04-30
【深度学习笔记】组卷积 2019-04-30
【深度学习笔记】循环神经网络和递归神经网络区别 2019-04-30
【学习笔记】英文科技论文常见英语句式积累 2019-04-30
【深度学习笔记】PixelShuffle 2019-04-30
【python3学习笔记】斜杠和双斜杠运算符的区别 2019-04-30
【深度学习笔记】torch.nn.Sequential(* args) 与 torch.nn.Module 2019-04-30
【深度学习笔记】用torch.nn.Sequential()搭建神经网络模型 2019-04-30
【深度学习笔记】用torch.nn.ModuleList搭建神经网络 2019-04-30
【解决错误】AttributeError: module ‘scipy.misc‘ has no attribute ‘imread‘ 2019-04-30
【解决错误】复现RCAN的时候遇到了ImportError: cannot import name ‘_update_worker_pids’ from ‘torch._C’ 2019-04-30
【解决错误】ModuleNotFoundError: No module named ‘skimage‘ 2019-04-30
【深度学习笔记】pytorch的点乘(dot product) 2019-04-30
【深度学习笔记】残差 2019-04-30
【错误解决】cv2.error: OpenCV(4.2.0) C:\projects\opencv-python\opencv\modules\imgproc\sr 2019-04-30