分布式秒杀方案--java

前提:先把商品详情和秒杀商品缓存redis中,减少对数据库的访问(可使用定时任务)

秒杀商品无非就是那几步(前面还可能会有一些判断,如用户是否登录,一人一单,秒杀时间验证等)

1一人一单

2.判断库存

3.减库存

4.创建订单

秒杀需要解决的问题?

1.解决超卖问题

1.1这样秒杀肯定会出现超卖的情况,所以必须加锁。加锁无非就两种锁,乐观锁和悲观锁。

悲观锁:直接在1前上锁即可,这里使用redisson里的可重入锁

java 复制代码
        // 获取分布式锁对象
        RLock lock = redissonClient.getLock("seckillLock");
        try {
            //等待5秒获取锁,执行60秒还是没有释放锁就强制释放
            boolean b = lock.tryLock(5L, 60L, TimeUnit.SECONDS);
            if (b){
               SeckillProductVo  seckillProductVo = seckillProductService.find(time, seckillId);
                //1.保证库存足够
                if (seckillProductVo.getStockCount()<=0){
                    return Result.error(SeckillCodeMsg.SECKILL_STOCK_OVER);
                }
              //下面两个方法必须写在一起避免事务未提交,未扣减库存就释放锁
            //2.扣减库存
            //3.生成订单
                orderInfo=orderInfoService.doSeckill(phone,seckillProductVo);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

1.2乐观锁:这个就不需要用代码演示了

1一人一单

2.判断库存

3.减库存

4.创建订单

在3.减库存的时候mysql语句 and 库存>0即可,MySQL会有自己的行锁。所以每次只会执行一次修改操作。以上两个方法都不会造成超卖

但是却未能解决秒杀问题,原因高并发请求都进入了数据库来查库,会造成数据库崩溃或阻塞其它mysql的执行。

2.重复下单问题,如何解决?

2.1 在4.创建订单后,把用户的唯一标识,id或phone存入redis的set中 key->{phone1,phone2}

redisTemplate.opsForSet().isMember(orderKey,phone)返回true或false

流程变成 1.一人一单 2.判断库存 3.减库存 4.创建订单 5.redisTemplate.opsForSet().add(orderKey,phone);

2.2 但是这样并不能完全解决重复下单,比如用户同时点了两次请求。极端条件下,a线程进入步骤1的判断,从1到4 需要进行很多操作,当a线程未走到5时,b线程就进来了这样也会造成重复下单。所以必须使用数据库的唯一索引进行解决,把用户唯一标识和秒杀商品唯一标识做成唯一索引即可解决

也就是在 4.创建订单 的时候会出现报错,3库存减掉之后数据回滚即可。

然而redis里的库存却和数据库的就不一样了,redis是无法被回滚的。后面会使用cannal对数据库和redis的库存进行同步。。。

有些人可能会说了,2.1没有解决重复问题,为什么不直接用2.2解决?2.1在大部分情况下都能解决,避免有人多次点击秒杀对数据造成压力,所以2.1和2.2一起使用才是最佳的选择

3.流量控制

3.1 必须把请求拦截在数据库之外,如果库存只有10个,也只有10个线程能最终到达数据库就是一个比较理想的方案

所以把数据库的秒杀商品库存存入redis中,进行预售,只有redis里能执行-1操作才能进入service,这样就极大的减少了请求到service层中

3.2 如果同一个秒杀时间段有多个商品 如100个秒杀商品,每个秒杀商品的库存为10个,那么就会有10*100个请求到达数据库,如果是淘宝的双11显然秒杀的商品会更多。那么我们就需要进行流量的削峰控制。

从controller中拦截消息,service取消息的时候慢慢拿,mq能够占时的让一部分请求存储在里面排队处理,service根据自己的处理能力去拿。有些人可能会说排队处理不是很慢?在controller中发完消息这个请求已经可以算是结束了,用户不会立即看到抢购结果。后面经过

最终方案

相关推荐
蜗牛互联网5 分钟前
AI给面试打分不够,求职者更需要可核对的证据
java·人工智能·后端
此时不提桶,更待何时28 分钟前
02-05-B-虚拟线程面试与生产事故实战
java·面试
名字还没想好☜31 分钟前
Spring Boot 3.2 RestClient 实战:替代 RestTemplate 调外部接口,超时、连接池与错误处理
java·后端·spring
Francek Chen32 分钟前
【大数据处理与分析】数据仓库Hive:04 数据仓库Hive概述
大数据·数据仓库·hive·hadoop·分布式
摇滚侠1 小时前
《Spring Boot 3:高级与架构设计》第 2 章 IOC容器的高级机制 BeanFactoryPostProcessor 个人理解 6
java·spring boot·笔记·后端
逆向编程1 小时前
QGIS地图投影(CRS坐标参照系)设置
开发语言
free-elcmacom1 小时前
发际线警告!手撕《C陷阱与缺陷》终极 BOSS:signal 函数与 typedef 魔法
c语言·开发语言·visual studio code·visual studio
程序员阿鹏1 小时前
简单介绍一下软引用
java·开发语言·jvm·数据结构·后端
SL_staff2 小时前
项目延期时如何用四类角色机制实现责任锚定?一线技术实践拆解
java·低代码·团队管理
wno7042 小时前
Spring Security Session管理
java·后端·spring