Redis+Lua——他叫了外援
Redis从2.6版本开始引入对Lua脚本的支持,通过在Redis服务器中嵌入Lua环境,Redis客户端可以使用Lua脚本,直接在服务端原子的执行多个Redis命令。
Lua
Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
为什么用
刚需
- 原子操作:Redis 使用单个 Lua 解释器去运行所有脚本,并且,Redis 也保证脚本会以原子性(atomic)的方式执行:当某个脚本正在运行的时候,不会有其他脚本或 Redis 命令被执行。
优势
- 高效且灵活:Redis的事务实现是基于观察者模式的check-and-set(乐观锁)。相对而言,引入Lua脚本的方式,可以轻松实现在事务方式中难以(无法)实现的业务。
- 减少网络开销:合并多次执行命令的网络请求,用一个请求完成,减少了网络资源开销和响应时间。
- 复用性:客户端发送的脚本会永久存储在Redis中,这意味着其他客户端可以复用这一脚本而不需要使用代码完成同样的逻辑。此特性基于 Script Load 命令。
应用
比如有这样一个需求:每当有新的用户注册到我的平台,为其分配一个客户经理,要求每分配30次,切换一个客户经理,我们只有两个客户经理可用。这个注册服务部署了N个实例。你可以考虑一下如何使用Redis的事务实现。
JAVA实现
/** 通过计数器获取客户经理姓名的lua脚本 */ /** 客户经理资源被抽象成node(环形/单向) */ static final String ASSIGN_ACCOUNT_MANAGER_LUA_SCRIPT = "local node1 = {next = nil, value = '王尼玛经理'}\n" + "local node2 = {next = node1, value = '王速卡经理'}\n" + "node1.next = node2\n" + "local currentNode = node1\n" + "\n" + "local currentAccountManagerkey = KEYS[1]\n" + "local currentAccountManager = redis.call('get', currentAccountManagerkey)\n" + "\n" + "if currentAccountManager then\n" + " if currentNode.value ~= currentAccountManager then\n" + " currentNode = currentNode.next\n" + " end\n" + "else\n" + " currentAccountManager = node1.value\n" + "end\n" + "\n" + "local counterKey = KEYS[2]\n" + "local counter = redis.call('incr', counterKey)\n" + "\n" + "if(tonumber(counter) == 30) then\n" + " redis.call('set', counterKey, 0)\n" + " redis.call('set', currentAccountManagerkey, currentNode.next.value)\n" + "end\n" + "\n" + "return currentAccountManager"; /** 客户经理姓名key*/ static final String CURRENT_ACCOUNT_MANAGER_KEY = "assignAccountManager:account_manager"; /** 客户经理分配计数器key*/ static final String COUNTER_KEY = "assignAccountManager:assign_counter";
jedis方式
@Component public class JedisUtil { private JedisUtil() { } @Autowired private JedisPool jedisPool; /** * 注册脚本。将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本 * 如果给定的脚本已经在缓存里面了,那么不执行任何操作。 * 在脚本被加入到缓存之后,通过 EVALSHA 命令,可以使用脚本的 SHA1 校验和来调用这个脚本。 * 脚本可以在缓存中保留无限长的时间,直到执行 SCRIPT FLUSH 为止。 * @param script 脚本字符串 * @return 脚本 sha id */ public String scriptLoad(String script) { Assert.hasLength(script, "parameter [script] must not be null"); Jedis jedis = null; String luaLoad = null; try { jedis = jedisPool.getResource(); luaLoad = jedis.scriptLoad(script); return luaLoad; } catch (Exception e) { } finally { if(jedis != null){ jedis.close(); } } return null; } /** * 根据给定的 sha1 校验码,执行缓存在服务器中的脚本 * @param scriptShaId 脚本 sha id * @param keys key列表 * @param args arg列表 * @return 脚本执行结果 */ public Object evalsha(String scriptShaId, List<String> keys, List<String> args) { Assert.hasLength(scriptShaId, "parameter [scriptShaId] must not be null"); Assert.notNull(keys, "parameter [keys] must not be null"); Assert.notNull(args, "parameter [args] must not be null"); Jedis jedis = null; Object val = null; try { jedis = jedisPool.getResource(); if (jedis.scriptExists(scriptShaId)) { val = jedis.evalsha(scriptShaId, keys, args); return val; } } catch (Exception e) { } finally { if(jedis != null){ jedis.close(); } } return null; } }
/** * 为新注册的用户分配客户经理 * @return 客户经理姓名 */ public String assignAccountManagerForNewAccount() { List<String> keys = Arrays.asList(CURRENT_ACCOUNT_MANAGER_KEY, COUNTER_KEY); return (String) jedis.evalsha(jedis.scriptLoad(ASSIGN_ACCOUNT_MANAGER_LUA_SCRIPT), keys, Collections.<String>emptyList()); }
RedisTemplate方式
@Autowired private StringRedisTemplate redisTemplate; /** * 为新注册的用户分配客户经理 * @return 客户经理姓名 */ public String assignAccountManagerForNewAccount() { List<String> keys = Arrays.asList(CURRENT_ACCOUNT_MANAGER_KEY, COUNTER_KEY); // 指定 lua 脚本,并且指定返回值类型 DefaultRedisScript<String> redisScript = new DefaultRedisScript<>(ASSIGN_ACCOUNT_MANAGER_LUA_SCRIPT, String.class); // 参数一:redisScript,参数二:key列表 return redisTemplate.execute(redisScript, keys); }
相对于文字描述,代码和适量的注释可以直抒胸臆
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
繁星计划&支付宝IoT小程序创新大赛正式启动
报名链接:https://dc.cloud.alipay.com/index#/iot 大赛简介 繁星计划IoT小程序创新大赛是由支付宝主办、阿里云协办,面向广大开发者的大型创新比赛,旨在鼓励开发者围绕在IoT设备上(蜻蜓+刷脸POS)可应用的营销类小程序(可含收单功能,但非仅有收单功能)进行技术与商业创新,通过IoT机具+营销小程序的软硬件结合,解决行业和商户痛点,为商户提供更多的符合行业特色的深度商业解决方案。优质的小程序模板最终会在支付宝服务市场、阿里云市场上线,供商户订购,完成IoT小程序的商业模式闭环。 大赛规则 作品要求 必须是在IoT蜻蜓或刷脸POS设备上跑通的小程序,仅有收单功能的小程序不能参赛 作品内容包含:方案PPT、Demo、小程序模板/方案、在IoT设备上跑通的演示视频、APPID 榜单权益 一、二、三等奖可以获得蜻蜓设备、阿里云无门槛代金券奖励,以及官方流量曝光资源 创意奖可以获得阿里云无门槛代金券奖励,以及官方流量曝光资源 大赛时间 大赛须知 参与创新大赛资格 大赛面向企业开发者,实名认证企业支付宝账号的企业可报名参赛 参赛者有开发团队,有小程序开发经验, ...
- 下一篇
Streams:深入理解Redis5.0新特性
概述 相较于Redis4.0,Redis5.0增加了很多新的特性,而streams是其中最重要的特性之一。streams是redis 的一种基本数据结构,它是一个新的强大的支持多播的可持久化的消息队列,在设计上借鉴了kafaka。streams的数据类型本身非常简单,有点类似于hash结构,但是它的额外特性异常强大且复杂: 支持持久化。streams能持久化存储数据,不同于pub/sub机制和list 消息被消费后就会被删除,streams消费过的数据会被持久化的保存在历史中。 支持多播。 这一点跟 pub/sub有些类似。 支持消费者组。streams 允许同一消费组内的消费者竞争消息,并提供了一系列机制允许消费者查看自己的历史消费消息。并允许监控streams的消费者组信息,消费者组内消费者信息,也可以监控streams内消息的状态。 基础内容 数据 ID streams 提供了默认的id模式用来唯一标识streams中的每一条数据,由两部分组成: <millisecondsTime>-<sequenceNumber> millisecondsTime是re...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS6,CentOS7官方镜像安装Oracle11G