通过redis的脚本lua如何实现抢红包功能_Redis

来源:脚本之家  责任编辑:小易  

1.完成所有工作,所有写操作处理完成后脚本会自动退出。2.脚本运行时出错并中途退出,所有以前执行的写操作都已发生,但不会再有其他写操作。3.Redis 通过 SHUTDOWN NOSAVE 关闭时(不保存)。4.你附加了调试器来“使”脚本完成#1 与#2(或其他手段来保证不会丢失数据)。对于使用数据库开发软件的人,我想你也认同只有情景#1 是最理想的。情景#2,#3,#4 都会导致数据异常(#2 与#4)和/或数据丢失(#3 和#4)。如果你很重视数据,你应该尽可能地阻止数据异常与丢失。这不是哲学,而是工作(This is not philosophy,this is doing your job)。但很遗憾目前的 Redis 也帮不了你多少。所以我决定改变这种情况www.zgxue.com防采集请勿采集本网。

redis 脚本介绍

主要用途是:(1)描述界面:WOW和剑网三的界面都是用LUA写的;(2)沟通引擎:游戏图形引擎提供了一些接口库,可以在LUA中调用;(3)服务器端:有些游戏,例如剑网三,在服务器端也会大量使用

Redis从2.6版本开始,通过内嵌支持Lua环境

除此之外Lua还可以使用redis.pcall函数实现对Redis的调用,redis.call和redis.pcall的不同在于,如果redis.call执行失败,那么脚本执行结束会直接返回错误,而redis.pcall会忽略错误继续执行脚本。

好处 减少网络开销。可以将多个请求通过脚本的形式一次发送,减少网络延迟 原子操作。redis将整个脚本当作一个整体去执行,中间不会被其他命令插入,无需担心脚本执行过程中会出现竞态条件 复用。客户端发送的脚本会永久保存在redis中,可以复用这一脚本

主要用途是:(1)描述界面:WOW和剑网三的界面都是用LUA写的;(2)沟通引擎:游戏图形引擎提供了一些接口库,可以在LUA中调用;(3)服务器端:有些游戏,例如剑网三,在服务器端也会大量使用LUA。

数据库表设计

不存在现成的方法 如果要实现你说的功能 一般来说 还是按照正常的方法通过insert 或remove对表进行值的增减 然后再额外的对表进行一个监听 当达到规定时间的时候,remove掉相关的值

简单两张表,一个红包表,一个红包领取记录表

使用Redis的脚本功能实现Redis中数据简单查询,有需要的朋友可以参考下。在Redis的设计中,key是一切,对于Redis是可见的,而value对于Redis来说就是一个字节数组,Redis并不知道你的value中存储

CREATE TABLE `t_red_envelope` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `amount` decimal(10,2) DEFAULT NULL COMMENT '金额', `num` int(11) DEFAULT NULL COMMENT '数量(分割成几分)', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COMMENT='红包'CREATE TABLE `t_red_envelope_record` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `user_id` bigint(20) DEFAULT NULL COMMENT '用户id', `reward` decimal(10,2) DEFAULT NULL COMMENT '领取到奖励', `red_envelope_id` bigint(20) DEFAULT NULL COMMENT '红包id', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COMMENT='红包领取记录'

代码编写

首先,生成一个红包,将其分成指定数量的随机小红包,以list结构(envelope:redEnvelopeId:红包id作为key)存储在reids中(以便抢红包弹出数据)

public Long divideRedEnvelope(int amount, int num) { /** * 每个人至少分到一分钱,如果有2000分,6人,随机得到五个小于1994(2000-6)的数 * 比如 a1=4,a2=120,a3=324,a4=500,a5=700(随机拿到的五个数进行排序),那么红包钱分别为: a1+1,a2-a1+1,a3-a2+1,a4-a3+1,a5-a4+1,1994-a5+1(总和刚好为2000) */ RedEnvelope redEnvelope = new RedEnvelope(); redEnvelope.setAmount(new BigDecimal(amount)); redEnvelope.setNum(num); redEnvelope.setCreateTime(new Date()); redEnvelope.setUpdateTime(new Date()); redEnvelopeDao.insert(redEnvelope); /** * 拿来随机分的,按分来算 */ int totalAmount = amount * 100 - num; /** * 随机数 */ int[] randomNum = new int[num - 1]; /** * 红包金额 */ int[] redEnvelopeAmount = new int[num]; for (int i = 0; i < num - 1; i++) { int rand = new Random().nextInt(totalAmount); randomNum[i] = rand; } Arrays.sort(randomNum); /** * 条件语句分别分配的第一个、最后一个、中间的红包 */ for (int i = 0; i < num; i++) { if (i == 0) { redEnvelopeAmount[i] = randomNum[i] + 1; } else if (i == num - 1) { redEnvelopeAmount[i] = totalAmount - randomNum[i - 1] + 1; } else { redEnvelopeAmount[i] = randomNum[i] - randomNum[i - 1] + 1; } } /** * 产生的小红包key,以list存储在reids中 */ String key = "envelope:redEnvelopeId:" + redEnvelope.getId(); Boolean flag = stringRedisTemplate.hasKey(key); if (!flag) { for (Integer i : redEnvelopeAmount) { stringRedisTemplate.opsForList().leftPush(key, i + ""); } } return redEnvelope.getId(); }

抢红包时,根据用户userId和红包id,生成KEYS[1]、KEYS[2]、KEYS[3] (存储小红包的key、领取红包记录的key、用户userId的key)传入脚本中。

​     1、先判断该用户是否抢过红包,有则返回-1,没有则从红包列表取出一个小红包

​     2、步骤1的小红包如果为空,则表明红包已经没抢光,返回 -2

​     3、否则返回取出的小红包金额

public String grabRedEnvelope(Long userId, Long redEnvelopeId) { DefaultRedisScript<String> redisScript = new DefaultRedisScript<>(); redisScript.setResultType(String.class); redisScript.setScriptText(LuaScript.redLua); List<String> keyList = new ArrayList(); /** * 产生的小红包key */ keyList.add("envelope:redEnvelopeId:" + redEnvelopeId); /** * 红包领取记录key */ keyList.add("envelope:record:" + redEnvelopeId); keyList.add("" + userId); keyList.add(String.valueOf(userId)); /** * -1 已经抢到红包 -2 红包已经完了 ,其余是抢到红包并返回红包余额 */ String result = stringRedisTemplate.execute(redisScript, keyList); return result; }

实现抢红包的Lua脚本

public class LuaScript { /** * -1 已经抢到红包 -2 红包被抢光 re 红包金额 ,keys[1]、keys[2]、keys[3]分别为存储小红包的key、红包领取记录key、用户id */ public static String redLua = "if redis.call('hexists',KEYS[2],KEYS[3]) ~=0 then \n" + " return '-1';\n" + " else \n" + "local re=redis.call('rpop',KEYS[1]);\n" + "if re then\n" + "redis.call('hset',KEYS[2],KEYS[3],1);\n" + "return re;\n" + "else\n" + "return '-2';\n" + "end\n" + "end";}

测试

首先通过接口分配红包生成一个100块、份额为10份的红包,并将其mysql数据库和redis

通过jmeter进行压测抢红包

结果

github代码链接

链接

总结

到此这篇关于通过redis的脚本lua如何实现抢红包功能的文章就介绍到这了,更多相关redis的脚本lua实现抢红包内容请搜索真格学网以前的文章或继续浏览下面的相关文章希望大家以后多多支持真格学网!

MySQL 与 Postgres在 1998-2003 年间,如果你想运行一个正规的数据库驱动的网站/服务,但又没有足够的资金购买微软或 Oracle 的数据库,你可以选择 MySQL 或 Postgres。很多人都选择了 MySQL,因为它速度较快—主要是因为 MyISAM 存储引擎没有提供事务功能以此来换取性能7a686964616fe4b893e5b19e31333363363435,但速度确实很快。另一些人转向 Postgres,因为虽然在相同硬件上其性能明显低于 MySQL,但 Postgres 不会丢失数据(说实话,MySQL 数据丢失的情况非常少见,但丢了可不是闹着玩的)。就这样凑合着过了很久;MySQL 将其默认的存储引擎从 MyISAM 过渡到了 InnoDB(其实很早就有了),这样它的存储引擎也得到了完整的事务支持和其他功能。与此同时,Postgres 也变快了,并添加了一个持续扩展的功能列表来使自己与众不同。现在对于 MySQL 与 Postgres 的选择只看个人的体验与偏好,除了有时业务需要或领导决定使用其他选择。数据完整性从很多方面来看,Redis 很像当初采用 InnoDB 前的 MySQL。而 Redis 采用了一种很合理的方式来保证数据完整性(复制,AOF 等),并且从 Redis2.6 开始引入的 Lua 脚本在功能与易用性方面为 Redis 的成长提供了很大助力。相对来说,Lua 脚本与其他数据库中的存储过程很相似,但脚本的执行有些许不同。在本文中最重要的一点就是一旦将脚本写入数据库,它会一直执行直到以下任一种情况出现:1.完成所有工作,所有写操作处理完成后脚本会自动退出。2.脚本运行时出错并中途退出,所有以前执行的写操作都已发生,但不会再有其他写操作。3.Redis 通过 SHUTDOWN NOSAVE 关闭时(不保存)。4.你附加了调试器来“使”脚本完成#1 与#2(或其他手段来保证不会丢失数据)。对于使用数据库开发软件的人,我想你也认同只有情景#1 是最理想的。情景#2,#3,#4 都会导致数据异常(#2 与#4)和/或数据丢失(#3 和#4)。如果你很重视数据,你应该尽可能地阻止数据异常与丢失。这不是哲学,而是工作(This is not philosophy,this is doing your job)。但很遗憾目前的 Redis 也帮不了你多少。所以我决定改变这种情况。实现 Lua 脚本事务我尝试解决上面列表中的#2,#3,#4 问题,最终像下面这样:脚本完成所有的工作,处理完写操作后正常退出脚本执行过程中遇到错误退出,不更改任何数据(所有写操作都回滚)无论有没有写入数据,都不会有数据丢失。这应该是所有的数据库都希望做到的,我打算把这个加到 Redis 中,因为我们都希望 Redis有 这个功能。目前的 pull request 只是一个概念性的证明。也就是说,为了避免数据丢失,你要么 a)显式使用事务的变体运行脚本,要么 b)强制所有 Lua 脚本调用带配置选项的事务语义。还有很多的办法使现在这个 patch 变得更好,我希望能得到 Salvatore(Redisw 作者)和其他社区的帮助内容来自www.zgxue.com请勿采集。


  • 本文相关:
  • 详解利用redis + lua解决抢红包高并发的问题
  • 简介lua脚本与redis数据库的结合使用
  • redis执行lua脚本的好处与示例代码
  • redis中如何使用lua脚本让你的灵活性提高5个逼格详解
  • 利用lua定制redis命令的方法详解
  • redis如何使用lua脚本实例教程
  • nginx利用lua+redis实现动态封禁ip的方法
  • 详解redis中lua脚本的应用和实践
  • redis和lua使用过程中遇到的小问题
  • 利用yum安装redis的方法详解
  • phpredis提高消息队列的实时性方法(推荐)
  • 详解redis命令和键_动力节点java学院整理
  • 关于redis状态监控和性能调优详解
  • redis实现分布式的方法总结
  • redis配置文件代码讲解
  • redis实现信息已读未读状态提示
  • redis源码解析:集群手动故障转移、从节点迁移详解
  • ubuntu 16.04安装redis的两种方式教程详解(apt和编译方式)
  • redis中5种数据结构的使用场景介绍
  • 为什么在 Redis 实现 Lua 脚本事务
  • 为什么在 Redis 实现 Lua 脚本事务
  • redis加载lua脚本,怎么获取数据
  • 为什么在 Redis 实现 Lua 脚本事务
  • redis lua脚本有什么用
  • redis 执行 xxx.luaxxx.lua 脚本如何传参数。格式是什么
  • redis集群支持lua脚本吗
  • 用lua 给redis 中塞值要怎么设置有效时间
  • redis 中有 4 亿条记录会占多少内存
  • Lua 中 变换数据类型。比如把数字变成string怎么写?
  • 网站首页网页制作脚本下载服务器操作系统网站运营平面设计媒体动画电脑基础硬件教程网络安全mssqlmysqlmariadboracledb2mssql2008mssql2005sqlitepostgresqlmongodbredisaccess数据库文摘数据库其它首页redis详解利用redis + lua解决抢红包高并发的问题简介lua脚本与redis数据库的结合使用redis执行lua脚本的好处与示例代码redis中如何使用lua脚本让你的灵活性提高5个逼格详解利用lua定制redis命令的方法详解redis如何使用lua脚本实例教程nginx利用lua+redis实现动态封禁ip的方法详解redis中lua脚本的应用和实践redis和lua使用过程中遇到的小问题利用yum安装redis的方法详解phpredis提高消息队列的实时性方法(推荐)详解redis命令和键_动力节点java学院整理关于redis状态监控和性能调优详解redis实现分布式的方法总结redis配置文件代码讲解redis实现信息已读未读状态提示redis源码解析:集群手动故障转移、从节点迁移详解ubuntu 16.04安装redis的两种方式教程详解(apt和编译方式)redis中5种数据结构的使用场景介绍超强、超详细redis数据库入门教程redis常用命令、常见错误、配置技redis操作命令总结redis中5种数据结构的使用场景介64位windows下安装redis教程redis中使用redis-dump导出、导入redis中统计各种数据大小的方法redis常用命令小结让redis在你的系统中发挥更大作用centos 6.6下redis安装配置记录redis基本类型和使用方法详解windows环境下redis+spring缓存实例讲解windows环境部署redis集群redis 密码设置和查看密码的方法redis教程(一):redis简介redis通过位图法记录在线用户的状态详解redis教程(十二):服务器管理命令总结redis+mysql+quartz 一种红包发送功能的实redislive监控redis服务的图文教程_动力节解决redis开启远程访问及密码问题
    免责声明 - 关于我们 - 联系我们 - 广告联系 - 友情链接 - 帮助中心 - 频道导航
    Copyright © 2017 www.zgxue.com All Rights Reserved