使用Redis事务与乐观锁、Lua脚本解决秒杀系统问题
2021/7/4 19:24:29
本文主要是介绍使用Redis事务与乐观锁、Lua脚本解决秒杀系统问题,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
该栏目会系统的介绍 Redis 的知识体系,共分为相关概念、操作指令、主从复制等模块
文章目录
- 事务简介
- 1、概述
- 2、特性
- 3、相关命令
- 秒杀案例
事务简介
1、概述
- :redis 事务是一个单独的隔离操作,就是用来串联多个命令防止别的命令插队
2、特性
- 单独的隔离操作
- 没有隔离级别
- 不保证原子性
3、相关命令
功能 | 指令 |
---|---|
组队 | multi |
执行 | exec |
取消组队 | discard |
监视 | watch |
取消监视 | unwatch |
秒杀案例
- 连接超时问题:使用连接池解决
- 超卖问题:使用事务和乐观锁
- 遗留库存问题:使用Lua脚本
/** * 1、使用连接池解决超时问题 * 2、使用事务与乐观锁解决超卖问题,但引入遗留库存问题,用Lua脚本解决 * * @param productId 商品Id * @param userId 用户Id * @return 操作结果 */ @Test public boolean secKill(String productId, String userId) { if (productId == null || userId == null) { return false; } // 定义key String stockKey = "sk:" + productId + ":stock"; String userKey = "sk:" + productId + ":user"; // 判断秒杀是否开始 final String stock = (String) stringOps.get(stockKey); if (stock == null) { System.out.println("秒杀活动还没开始,请等待"); return false; } // 判断用户是否已经秒杀过 final Boolean result = Optional.ofNullable(setOps.isMember(userKey, userId)).get(); if (result) { System.out.println("用户已经参加过秒杀了"); return false; } // 判断秒杀是否结束 if (Integer.parseInt(stock) <= 0) { System.out.println("秒杀结束..."); return false; } // 监视库存,使用乐观锁机制 redisTemplate.watch(stockKey); // 开启事务 redisTemplate.multi(); stringOps.decrement(stockKey); setOps.add(userKey, userId); final List<Object> results = redisTemplate.exec(); if (results.size() == 0) { System.out.println("秒杀失败"); return false; } System.out.println("秒杀成功"); return true; } /** * LUA脚本概述:将复杂的或者多步的redis操作,写为一个脚本,一次提交给redis执行,减少反复连接 * redis的次数。提升性能。LUA脚本是类似redis事务,有一定的原子性,不会被其他命令插队,可以完 * 成一些redis事务性的操作 * * 使用Lua脚本解决遗留库存问题 * * @param productId 商品Id * @param userId 用户Id * @return 操作结果 */ @Test public boolean secKillScript(String productId, String userId) { String secKillScript = "local userid=KEYS[1];\r\n" + "local prodid=KEYS[2];\r\n" + "local qtkey='sk:'..prodid..\":qt\";\r\n" + "local usersKey='sk:'..prodid..\":usr\";\r\n" + "local userExists=redis.call(\"sismember\",usersKey,userid);\r\n" + "if tonumber(userExists)==1 then \r\n" + " return 2;\r\n" + "end\r\n" + "local num= redis.call(\"get\" ,qtkey);\r\n" + "if tonumber(num)<=0 then \r\n" + " return 0;\r\n" + "else \r\n" + " redis.call(\"decr\",qtkey);\r\n" + " redis.call(\"sadd\",usersKey,userid);\r\n" + "end\r\n" + "return 1"; // 定义key String stockKey = "sk:" + productId + ":stock"; String userKey = "sk:" + userId + ":user"; // 执行Lua脚本 DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); redisScript.setScriptText(secKillScript); redisScript.setResultType(Long.class); final long result = Optional.ofNullable(redisTemplate.execute( redisScript, Arrays.asList(userId, productId))).get(); if (0 == result) { System.out.println("秒杀活动结束"); return false; } else if (1 == result) { System.out.println("秒杀成功"); return true; } else if (2 == result) { System.out.println("用户已经参加过秒杀了"); return false; } else { System.out.println("秒杀异常!"); return false; } }
这篇关于使用Redis事务与乐观锁、Lua脚本解决秒杀系统问题的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-09-26阿里云Redis项目实战:新手入门教程
- 2024-09-26阿里云Redis资料入门教程
- 2024-09-25阿里云Redis入门教程:快速掌握Redis的基本操作
- 2024-09-25阿里云Redis学习:新手入门教程
- 2024-09-21Redis资料入门教程:轻松掌握Redis基础知识
- 2024-09-21Redis资料:入门级用户必学教程
- 2024-09-21Redis资料:新手入门教程与实践指南
- 2024-09-20Redis教程:从入门到实践的全面指南
- 2024-09-20Redis教程:初学者快速入门指南
- 2024-09-20Redis教程:新手入门与实践指南