iis服务器助手广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >redislua脚本实战秒杀和减库存的实现
  • 417
分享到

redislua脚本实战秒杀和减库存的实现

2024-04-02 19:04:59 417人浏览 八月长安
摘要

目录前言1.Redisson介绍2. redis lua脚本编写与执行3.redis减库存lua脚本4.实战4.1 减库存逻辑4.2 压测前言 我们都知道redis是高性能高并发系统

前言

我们都知道redis是高性能高并发系统必不可少的kv中间件,它以高性能,高并发著称,我们常常用它做缓存,将热点数据或者是万年不变的数据缓存到redis中,查询的时候直接查询redis,减轻db的压力,分布式系统中我们也会拿它来做分布式,分布式id,幂等来解决一些分布式问题,redis也支持lua脚本,而且能够保证lua脚本执行过程中原子性,这就使得它的应用场景很多,也很典型,在redisson这个redis客户端中,它的各种分布式锁底层就是使用lua来实现的。本文主要是学习一下redis lua脚本的编写,以及在redisson这个redis客户端中是怎样使用的,实战一下秒杀场景redis减库存lua脚本的编写,并伪真实环境压测查看效果。

1.redisson介绍

redisson是一个redis的客户端,它拥有丰富的功能,而且支持redis的各种模式,什么单机,集群,哨兵的都支持,各种各样的分布式锁实现,什么分布式重入锁,红锁,读写锁,信号量的,然后它操作redis的常用数据结构就跟操作jdk的各种集合一样简单。
这里我们稍微演示下,不做过多的介绍,api毕竟只是个api,有意思的都在它背后各种原理。

Maven依赖


<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.8.1</version>
</dependency>

创建RedissonClient对象


Config config = new Config();
config.useSingleServer()
        .setAddress("redis://xxx:xx");
RedissonClient redissonClient = Redisson.create(config);

它的功能超级多,下面只是列举了一些常用的,还有什么Bloom过滤器,队列等等。


// string
redissonClient.getBucket("name").set("zhangsan");
redissonClient.getBucket("name").get();

// hash
RMap<Object, Object> user = redissonClient.getMap("user");
user.put("name","zhangsan");
user.put("age",11);

// list
RList<String> names = redissonClient.getList("names");
names.add("zhangsan");
names.add("lisi");
names.add("wangwu");
// set
RSet<Object> nameSet = redissonClient.getSet("names");
nameSet.add("lisi");
nameSet.add("lisi");

//lock
RLock lock = redissonClient.getLock("lock");
lock.lock();
lock.unlock();

// 分布式id
RAtomicLong id = redissonClient.getAtomicLong("id");
long l = id.incrementAndGet();  

2. redis lua脚本编写与执行

其实redis中的lua脚本并不难,你也不需要把lua语言再去重学一遍,全凭感觉就好了,使用的时候去查下语法就ok了。
脚本中就一个redis.call() 应该算是函数吧(方法也可以),比如我要使用lua脚本实现set动作,就可以这样写


return redis.call('set','name','zhangsan');

其实就是跟redis交互命令一个样子,再使用lua语言做一些条件分支,循环啥的,就完成了一些稍微复杂的逻辑。
下面自己写一遍扣减库存的逻辑,你就会这个玩意了。
上面是介绍了lua脚本的编写,下面我们介绍下这个执行。
不管是redis自己带的那个客户端,还是jedis,jediscluster,redistemplate,redisson这些客户端都是支持lua脚本api的,其实就是eval,evalsha,scriptload 这几个命令用的比较频繁,我这里把菜鸟教程上面关于介绍redis脚本命令截图过来

在这里插入图片描述

多说无益,这里直接使用redisson客户端实践一下。


public class RedisLua {
    private static  final Config config ;
    private static  final RedissonClient redisson ;
    static {
        config = new Config();
        config.useSingleServer()
                .setAddress("redis://ip:port");

        redisson =  Redisson.create(config);
    }
    public static void main(String[] args) throws InterruptedException {
        redisson.getBucket("name").set(11);
        RScript script = redisson.getScript();
        String result = script.eval(RScript.Mode.READ_ONLY, new StrinGCodec(),"return redis.call('get','name');", RScript.ReturnType.VALUE);
        System.out.println(result);
    }
}

可以看到就是使用getScript方法获取一个script对象,然后调用script 对象的eval方法,这个script对象其实还是用好多个方法的,evalSha等等,可以自己研究下。然后就是通过lua脚本获取我上面set进去的name值。
我们在看上面菜鸟教程对脚本命令的介绍的时候,还发现有key… arg…这些东西,这个我认为就是动态替换(传参)的。
比如我现在不获取name这个key的值了,我要获取age 的,或者是我要直接set一个值,这个时候我就可以lua脚本中有两个变量与你传的参数对等起来
KEYS[1] ARGV[1]
你可以看作是数组,不过它的位置是从1开始的。


RScript script = redisson.getScript();
List<Object> keys=new ArrayList<>();
keys.add("age");
String re = script.eval(
        RScript.Mode.READ_WRITE,
        new StringCodec(),
        "return redis.call('set',KEYS[1],ARGV[1]);",
        RScript.ReturnType.VALUE,
        keys,1);
Object age = redisson.getBucket("age").get();
System.out.println(age);

KEYS[1] 就对应这age, ARGV[1]就对应1,同理,KEYS[2] 就对应keys集合中的第二个元素。

3.redis减库存lua脚本

先介绍下下单减库存是怎么干的吧,其实一般库存有可用库存与预占库存,再下单的时候,就将可用库存减去你购买的商品数量看看是否是小于0,如果是小于0的话,说明库存不够了,就不让下单购买了,如果可用库存充足,可用库存减去购买商品数量,预占库存加上你购买商品数量,当用户超时未支付或者是手动取消订单的时候,就会去预占库减去用户购买商品数,可用库存加上商品数,其实还有一个已售库存,商家发货,已售库存加上商品数,预占减去商品数,大体上是这个逻辑。
现在可以想下,如果让你来实现下单预占库存的功能,你会怎么做,数据库三个库存字段这个不用说了。
首先你得先把可售库存查询出来,然后与购买商品数量进行比较,如果是可售库存大于这个购买商品数量,就可以购买,更新可售库存与预占库存。
如果上面这段代码逻辑你不加一些特殊手段的处理,那ok,高并发场景下绝对会出现超卖现象。
如果是秒杀场景呢?血亏。
这个时候我们可能会增加一些特殊手段来解决,比如说加锁,加分布式锁,将这一段的业务逻辑锁住,这个时候就不会出现那种超卖现象了, 但是这个中情况如果售罄的话,也会一直查询数据库。秒杀的时候,流量那么高,你不能让这么大的流量直接查库,如果商品售罄直接返回就可以了,不用再查询数据库了。
一般秒杀的时候,会将商品的库存同步推到redis中,流量过来的时候,会先扣减redis的库存,如果redis成功了,才扣减数据库中的库存,如果redis中的库存没了,直接返回就ok,这样大流量就不会直接冲击数据库了,那么redis要实现这段逻辑的话,就需要lua脚本的原子性了。
接下来我们就实现一下lua脚本扣减库存逻辑。


public static final String LOCK_STOCK_LUA=  "local counter = redis.call('hget',KEYS[1],ARGV[1]); \n" +
                                                "local result  = counter - ARGV[2];" +
                                                "if(result>=0 ) then \n" +
                                                "   redis.call('hset',KEYS[1],ARGV[1],result);\n" +
                                                "   redis.call('hincrby',KEYS[1],ARGV[3],ARGV[2]);\n" +
                                                "   return 1;\n" +
                                                "end;\n" +
                                                "return 0;\n";

我这里已经写好了,直接贴出来。
数据设计大体是这个样子的,使用hash数据结构
商品:{
“可售库存”:100,
“预占库存”:0,
“已售库存”:0
}


local counter = redis.call('hget',KEYS[1],ARGV[1]);

获取可售库存数量


local result  = counter - ARGV[2];
if(result>=0 ) then

可售库存减去要购买的商品数量,如果是大于0的话,说明库存还够。


redis.call('hset',KEYS[1],ARGV[1],result);
redis.call('hincrby',KEYS[1],ARGV[3],ARGV[2]);
return 1;

重新设置可售库存数量,增加预占库存,然后返回1
如果是库存不够的话,直接返回0了就。

4.实战

4.1 减库存逻辑

减库存逻辑其实就是先是用lua脚本减redis库存,如果成功再去减数据库中的真实库存,如果减redis库存失败,库存不足,就不会再走后面减真实库存的逻辑了。
这块的话,我是写了一个库存服务,实现了这段逻辑,但是总感觉有各种数据不一致的问题,当然不是超卖,而是少卖问题,这里就不发出来了。

4.2 压测

我们这个实战是在阿里云进行的
redis选的是容器服务,按秒计费,配置是0.5c1g
Mysql也是选择的容器服务,配置是0.5c1g
库存服务是云服务器,按小时计费的那种,配置是2c4g,因为要部署多个服务跟实例,选择的比较大。
压测也是使用的阿里云的性能测试服务。

在这里插入图片描述

在这里插入图片描述

redis监控,可以看到,这点并发对redis来说就是毛毛雨。cpu才使用7%

在这里插入图片描述

云服务器这块手速有点慢,没截图出来,cpu跟内存都在50%左右。

在这里插入图片描述

mysql数据库,可以看到cpu飙上去了,内存飙上去了。

在这里插入图片描述

数据库数据:

在这里插入图片描述

可以发现并没有出现超卖现象。

到此这篇关于redis lua脚本实战秒杀扣减库存的实现的文章就介绍到这了,更多相关redis lua战秒杀扣减库存内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: redislua脚本实战秒杀和减库存的实现

本文链接: https://www.lsjlt.com/news/158255.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • redislua脚本实战秒杀和减库存的实现
    目录前言1.redisson介绍2. redis lua脚本编写与执行3.redis减库存lua脚本4.实战4.1 减库存逻辑4.2 压测前言 我们都知道redis是高性能高并发系统...
    99+
    2024-04-02
  • redis lua脚本实战和减库存的实现是怎样的
    这篇文章给大家介绍redis lua脚本实战和减库存的实现是怎样的,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。前言我们都知道redis是高性能高并发系统必不可少的kv中间件,它以高性能,高并发著称,我们常常...
    99+
    2023-06-21
  • 如何通过redis减库存的秒杀场景实现
    目录使用思路:第一步:系统初始化后就将所有商品库存放入缓存第二步: 预减库存从缓存中减库存内存标记Redis扣库存,主要目的是减少对数据库的访问,之前的减库存,直接访问数据库,读取库...
    99+
    2024-04-02
  • Python淘宝秒杀的脚本实现
    准备工作 我们需要把秒杀的商品加入购物车,因为脚本点击的是全选,所以不需要的商品要移出购物车。 过程分析 1.打开某宝网站; pq = webdriver.Chrome() pq.g...
    99+
    2023-01-06
    Python淘宝秒杀 Python 秒杀
  • 怎么通过redis实现减库存的秒杀场景
    这篇“怎么通过redis实现减库存的秒杀场景”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“怎么通过redis实现减库存的秒杀...
    99+
    2023-06-30
  • Python淘宝或京东等秒杀抢购脚本实现(秒杀脚本)
    目录一、环境二、安装1.ChromeDriver安装2.Seleuinm安装3.淘宝秒杀脚本4.京东秒杀脚本总结我们的目标是秒杀淘宝或京东等的订单,这里面有几个关键点,首先需要登录淘...
    99+
    2024-04-02
  • 备战618!用Python脚本帮你实现淘宝秒杀
    selenium 安装与 chromedriver安装 我们前文提到,Python脚本中使用了selenium库,而selenium又通过chromedriver来控制浏览器的鼠标点...
    99+
    2024-04-02
  • JS脚本如何实现网页自动秒杀点击
    这篇文章主要为大家展示了“JS脚本如何实现网页自动秒杀点击”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“JS脚本如何实现网页自动秒杀点击”这篇文章吧。代码如下:...
    99+
    2024-04-02
  • 基于MySQL和Redis扣减库存的实践
    目录背景环境搭建后台系统中间件测试工具扣减模式基于数据库行锁 + CAS 实现库存的扣减基于 Redis 实现库存的扣减总结背景 在很多情况下,扣减库存是一个十分常见的需求,例如:学...
    99+
    2023-05-19
    MySQL 扣减库存 Redis扣减库存
  • 怎么用vbs实现强制杀进程的脚本
    这篇文章主要讲解了“怎么用vbs实现强制杀进程的脚本”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用vbs实现强制杀进程的脚本”吧!'备注:vbs强制关闭程序 '防止出现...
    99+
    2023-06-08
  • 怎么实现每隔30秒循环收集MYSQL备库状态信息的SHELL脚本
    这篇文章主要介绍了怎么实现每隔30秒循环收集MYSQL备库状态信息的SHELL脚本,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。 ...
    99+
    2024-04-02
  • 如何使用Redis实现电商系统的库存扣减
    目录解决方案分析基于数据库单库存基于数据库多库存基于redis基于redis实现扣减库存的具体实现在日常开发中有很多地方都有类似扣减库存的操作,比如电商系统中的商品库存,抽奖系统中的...
    99+
    2024-04-02
  • 如何实现备份数据库的shell脚本
    这篇文章将为大家详细讲解有关如何实现备份数据库的shell脚本,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。备份数据库的shell脚本一代码如下:#!/bin/bash#指定运行的脚本shell#运行脚本...
    99+
    2023-06-09
  • 如何实现数据库的检查步骤脚本
    小编给大家分享一下如何实现数据库的检查步骤脚本,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!1、数据库的实例名版本        Picasa Co...
    99+
    2024-04-02
  • 如何实现统计cpu内存和使用率的shell脚本代码
    这篇文章主要介绍了如何实现统计cpu内存和使用率的shell脚本代码,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。代码如下:#!/bin/shclearwhile ((1&g...
    99+
    2023-06-09
  • Redis实现库存扣减的解决方案防止商品超卖
    目录Redis 如何实现库存扣减操作?如何防止商品被超卖?解决方案1. 使用mysql数据库2. 还是使用数据库3. 将库存放到redis使用redis的incrby特性来扣减库存。...
    99+
    2024-04-02
  • 如何实现释放CentOS系统内存的Shell脚本
    本篇内容介绍了“如何实现释放CentOS系统内存的Shell脚本”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!脚本内容:代码如下: ...
    99+
    2023-06-09
  • 如何实现每隔30秒循环收集innodb状态信息的VB脚本
    这篇文章主要为大家展示了“如何实现每隔30秒循环收集innodb状态信息的VB脚本”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何实现每隔30秒循环收集inn...
    99+
    2024-04-02
  • 如何实现每隔30秒循环收集innodb状态信息的SHELL脚本
    这篇文章给大家分享的是有关如何实现每隔30秒循环收集innodb状态信息的SHELL脚本的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。 x=0whi...
    99+
    2024-04-02
  • Go 缓存系统的架构设计:重定向和 Bash 脚本的实现方法
    在计算机科学领域,缓存系统是一种常见的技术,用于提高系统的性能和响应速度。在 Go 语言中,有许多优秀的缓存系统,其中一些基于内存,而另一些则基于磁盘或者网络存储。本文将介绍一种基于内存的缓存系统,它使用重定向和 Bash 脚本来实现。 ...
    99+
    2023-08-28
    缓存 重定向 bash
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作