Redis(二十四)-秒杀案例之库存遗留问题解决

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 上一篇文章Redis(二十三)-秒杀案例之超卖和超时问题解决我们介绍了超卖和超时问题的解决,最后还留了一个问题—库存遗留问题。这篇文章就来介绍下如何解决库存遗留问题。

简介

上一篇文章Redis(二十三)-秒杀案例之超卖和超时问题解决

我们介绍了超卖和超时问题的解决,最后还留了一个问题—库存遗留问题。这篇文章就来介绍下如何解决库存遗留问题。

库存遗留问题的原因分析

利用乐观锁之所以出现库存遗留问题,在高并发情况下,如果两千个人同时获取到V1.0版本的数据,然后同时提交的话,那么最终将只会有一个人修改成功,其余的1999人都会修改失败。

库存遗留问题解决

库存遗留问题可以通过LUA脚本解决。LUA脚本就是将复杂的或者多步的redis操作,写成一个脚本,一次提交给Redis执行,减少反复连接redis的次数。提升性能。

LUA脚本是类似redis事务,有一定的原子性,不会被其他命令插队,可以完成一些redis事务性的操作。

但是注意redis的lua脚本功能,只有在Redis 2.6以上的版本才可以使用

利用lua脚本淘汰用户,解决超卖问题。

redis 2.6版本以后,通过lua脚本解决争抢问题,实际上是redis利用其单线程的特性,用任务队列的方式解决多任务并发问题。

lua脚本

通过lua脚本将减库存decr命令和淘汰用户的命令sadd放在同一个脚本中,保证了命令的原子性。

local userid=KEYS[1];
local prodid=KEYS[2];
local qtkey="sk:"..prodid..":qt";
local userskey="sk:"..prodid..":user";
local userExists=redis.call("sismember",userskey,userid);
if tonumber(userExists)==1 then
  return 2;
end
local num=redis.call("get",qtkey);
if tonumber(num)<=0 then
  return 0;
else
  redis.call("decr",qtkey);
  redis.call("sadd",userskey,userid);
  end
  return 1

基本逻辑跟前面使用乐观锁实现是一样的。

1.首先判断当前用户userid是否存在,如果存在返回2;

2.判断库存是否存在,如果库存小于等于0,则返回0;

3.执行扣减库存和淘汰用户的命令。

代码实现

将前面定义的lua脚本以字符串常量的形式定义。接着在doSecKill方法中执行该lua脚本。

@Service
public class SeckillByScript {
    static String secKillScript1 = "local userid=KEYS[1];\n" +
            "local prodid=KEYS[2];\n" +
            "local qtkey=\"sk:\"..prodid..\":qt\";\n" +
            "local userskey=\"sk:\"..prodid..\":user\";\n" +
            "local userExists=redis.call(\"sismember\",userskey,userid);" +
            "if tonumber(userExists)==1 then\n" +
            "  return 2;\n" +
            "end\n" +
            "local num=redis.call(\"get\",qtkey);\n" +
            "if tonumber(num)<=0 then\n" +
            "  return 0;\n" +
            "else\n" +
            "  redis.call(\"decr\",qtkey);\n" +
            "  redis.call(\"sadd\",userskey,userid);\n" +
            "  end\n" +
            "  return 1";
    public boolean doSecKill(String userid, String prodid) {
        JedisPool jedisPool = JedisPoolUtil.getJedisPool();
        Jedis jedis = jedisPool.getResource();
        String sha1 = jedis.scriptLoad(secKillScript1);
        Object result = jedis.evalsha(sha1, 2, userid, prodid);
        String reString = String.valueOf(result);
        if ("0".equals(reString)) {
            System.out.println("已抢空!");
            return false;
        } else if ("1".equals(reString)) {
            System.out.println("抢购成功");
            return true;
        } else if ("2".equals(reString)) {
            System.out.println("该用户已抢过!");
            return false;
        } else {
            System.out.println("抢购异常");
            return false;
        }
    }
}

最后就是在controller中定义一个接口来调用该方法。通过ab工具模拟并发

ab -n 1000 -c 100 -T 'application/x-www-form-urlencoded' http://192.168.3.69:8080/seckill/skill/do2

最终的执行结果正常。

总结

本文详细介绍了如何通过lua脚本来实现秒杀案例


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
7天前
|
缓存 NoSQL Java
Redis7的10大应用场景和案例解析
你在项目中使用 Redis 实现了什么应用场景,欢迎一起跟 V 哥讨论。同时也做个小调查,朋多少兄弟是需要了解 Redis 核心源码的,人多的话,下一篇 V 哥写 Redis7的源码分析,人少的话就算了,感谢。
|
2月前
|
存储 监控 NoSQL
【Redis技术专区】「优化案例」谈谈使用Redis慢查询日志以及Redis慢查询分析指南
【Redis技术专区】「优化案例」谈谈使用Redis慢查询日志以及Redis慢查询分析指南
31 0
|
2月前
|
缓存 NoSQL 前端开发
【Redis技术专区】「实战案例」谈谈使用Redis缓存时高效的批量删除的几种方案
【Redis技术专区】「实战案例」谈谈使用Redis缓存时高效的批量删除的几种方案
40 0
|
2月前
|
NoSQL Java 数据库
优惠券秒杀案例 - CAS、Redis+Lua脚本解决高并发并行
优惠券秒杀案例 - CAS、Redis+Lua脚本解决高并发并行
102 0
|
4月前
|
NoSQL 安全 Redis
解决秒杀系统库存超卖问题:乐观锁与Redis分布式锁的应用
解决秒杀系统库存超卖问题:乐观锁与Redis分布式锁的应用
514 0
|
4月前
|
存储 NoSQL 关系型数据库
Redis系列-8.Redis案例实战之Bitmap、Hyperloglog、GEO(下)
Redis系列-8.Redis案例实战之Bitmap、Hyperloglog、GEO
30 0
|
4月前
|
存储 NoSQL 算法
Redis系列-8.Redis案例实战之Bitmap、Hyperloglog、GEO(上)
Redis系列-8.Redis案例实战之Bitmap、Hyperloglog、GEO
53 0
|
10月前
|
存储 缓存 NoSQL
案例01-修改数据redis没有同步更新
修改数据redis没有同步更新
|
6月前
|
消息中间件 缓存 NoSQL
阿里云国际站代理商:Redis实现分布式配置管理的方法与应用案例
@luotuoemo飞机@TG阿里云国际站代理商:Redis实现分布式配置管理的方法与应用案例,为了实现高可用和负载均衡,可以将Redis部署成哨兵集群或集群模式。哨兵负责监控主从节点的状态,发现故障时自动进行故障转移。集群模式可以提高系统的可扩展性,通过添加更多的从节点来分摊负载压力。
|
7月前
24Redis - 事务测试案例
24Redis - 事务测试案例
35 0