联系我们
简单又实用的WordPress网站制作教学
当前位置:网站首页 > 程序开发学习 > 正文

使用Redis+Lua通过原子减解决超卖问题[示例]

作者:小教学发布时间:2023-09-19分类:程序开发学习浏览:78


导读:系列文章目录一、SpringBoot连接MYSQL数据库实例[tk.mybatis连接MYSQL数据库]二、SpringBoot连接Redis与Redisson[代码]三、Spri...

系列文章目录

一、SpringBoot连接MYSQL数据库实例[tk.mybatis连接MYSQL数据库]
二、SpringBoot连接Redis与Redisson[代码]
三、SpringBoot整合WebSocket[代码]
四、使用Redis+Lua通过原子减解决超卖问题[示例]
五、SpringBoot整合ElasticSearch[代码示例]

文章目录

  • 系列文章目录
  • 前言
  • 一、准备工作
  • 二、不使用Lua
  • 三、使用Lua


前言

超卖,即在并发的情况下,所售商品数量大于商品的库存数量.在并发量大的情况下,用户请求同时到达,对数据库进行操作,在没有采取相应的处理的情况时从而导致出现超卖现象.

一、准备工作

在redis中放入十件商品

使用Redis+Lua通过原子减解决超卖问题[示例]


二、不使用Lua

使用20个线程抢商品

public void test () {
    ExecutorService service = Executors.newFixedThreadPool(20);
    for (int i = 0; i < 20; i++) {
        int finalI = i;
        service.execute(new Runnable() {
            @Override
            public void run() {
                if (Integer.parseInt(String.valueOf(redisUtils.get("test"))) > 0) {
                    int execute = Integer.parseInt(String.valueOf(redisUtils.decr("test", 1)));
                    if (execute != 0) {
                        log.info("线程" + finalI + "抢到了商品!!!");
                    } else {
                        log.info("线程" + finalI + "未抢到商品");
                    }
                } else {
                    log.info("商品数量不足");
                }
            }
        });
    }
}

使用Redis+Lua通过原子减解决超卖问题[示例]
运行代码发现已经超出了十个人抢到了商品.
此时redis的存值已经变为了负数,出现了超卖的情况.

使用Redis+Lua通过原子减解决超卖问题[示例]


三、使用Lua

public void test_lua () {
    StringBuilder sb = new StringBuilder();
    sb.append("if (redis.call('exists', KEYS[1]) == 1) then");    // 判断key是否存在
    sb.append("    local stock = tonumber(redis.call('get', KEYS[1]));");   // 获取锁
    sb.append("    if (stock == -1) then");
    sb.append("        return 1;");
    sb.append("    end;");
    sb.append("    if (stock > 0) then");
    sb.append("        redis.call('decrby', KEYS[1], 1);");    // 商品数量减1
    sb.append("        return stock;");
    sb.append("    end;");
    sb.append("    return 0;");
    sb.append("end;");
    sb.append("return -1;");
    String STOCK_LUA = sb.toString();

    DefaultRedisScript<Long> objectDefaultRedisScript = new DefaultRedisScript<>();
    objectDefaultRedisScript.setScriptText(STOCK_LUA);
    objectDefaultRedisScript.setResultType(Long.class);

    ArrayList<String> keys = new ArrayList<>();     // 脚本中的KEYS参数
    keys.add("test");

    ExecutorService service = Executors.newFixedThreadPool(20);
    for (int i = 0; i < 20; i++) {
        int finalI = i;
        service.execute(new Runnable() {
            @Override
            public void run() {
                int execute = Integer.parseInt(redisTemplate.execute(objectDefaultRedisScript, keys).toString());
                if (execute != 0) {
                    log.info("线程" + finalI + "抢到了商品!!!");
                } else {
                    log.info("线程" + finalI + "未抢到商品");
                }
            }
        });
    }
}

依然使用20个线程抢商品,运行代码只有10个线程抢到了商品
使用Redis+Lua通过原子减解决超卖问题[示例]
此时Redis中的存值为%0
使用Redis+Lua通过原子减解决超卖问题[示例]




标签:的博客


程序开发学习排行
最近发表
网站分类
标签列表