秒杀项目下的各种并发情况解决

1.超卖问题

本文章以黑马点评中抢购优惠券这个案例来讲。算是对该项目的秒杀模块的总结,以黑马点评项目为背景,但不限于该项目。

一般情况下,抢购逻辑:

在高并发的情况下,多个线程可能会同时拿到数据库中的值,每个线程减一,但同时写入数据库的话,抢了三件,但实际是数据库中数量减一,这时不可取的。

所以,我们要对数据操作进行加锁:

悲观锁:

一个线程拿到锁,其他线程进行等待,这样可以解决超卖问题,但多个线程阻塞在那里,这显然是不划算的,性能很低

乐观锁:

每次进行数据操作,都带上乐观锁版本号,如果这次修改版本号与一开始获取的版本号不一致,就执行失败。这样不会阻塞,性能更好。

2.一人一单

当有些优惠券只限于一人只能抢一单时,该如何实现?

实现方案:

先查询订单表——如果订单表中有记录,则无法不能继续下单

缺点:

由于是多线程,高并发的情况下,查询操作,和之后下单操作中,有可能在判断订单表时,有其他 线程已经插入了订单,而这时你恰好判断完毕,这时依旧可以抢到优惠卷,从而无法达到一人一单的效果。

解决方案:

对查询订单表,插入订单表行为进行加锁,同时对该业务进行事务封装,保证原子性,注意spring中事务的失效。

        Long userId = UserHolder.getUser().getId();
        synchronized (userId.toString().intern()) {
           //由于这里是当前方法,并没有事务注解,拿到的是当前对象,而事务是要拿到代理对象,通过代理对象实现事务。所以这样做会导致事务失效
            return this.createVoucherOrder(voucherId);
           // 拿到当前类的代理对象
           IVoucherOrderService o = (IVoucherOrderService)AopContext.currentProxy();
           return o.createVoucherOrder(voucherId);
        }

3.分布式锁

JVM中是通过锁监视器来实现锁机制,但多进程下,每个服务的锁监视器是不一样的,所以在分布式的情况下,我们要想办法让每个进程的锁监视器是一样的,这样就能实现分布式锁

 实现方案:

通过redis这个中间件。setnx来实现开闭锁。通过设置过期时间,来避免死锁问题。

 分布式锁中的各种极端情况:

1.当业务执行时间超过key过期时间,导致锁过期,其他线程进来由于锁过期,它能继续拿到锁,然而当拿到锁后该线程执行业务时,A线程业务执行好了,删除了锁,但由于A一开始拿到的锁已经过期了,所以现在执行的删除锁操作,删的是其他线程的锁。

解决思路:

在删除锁时进行判断,判断是否是当前线程的锁:

2.当判断完是否是该线程的锁时,恰好锁过期,并且另一个线程介入,此时再执行删除操作,依旧会误删

解决方案:将判断和删除原子化。通过lua脚本实现

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值