电商防止库存超卖解决方案

1 悲观锁解决方案    

    悲观锁,也就是在修改数据的时候,采用锁定状态,排斥外部请求的修改。遇到加锁的状态,就必须等待。可以采用redis队列+mysql事务控制的方案,下面是流程图:

    mysql的执行代码:

beginTranse(开启事务)
try{
    //quantity为请求减掉的库存数量
    $dbca->query('update s_store set amount = amount - quantity where postID = 12345');
    $result = $dbca->query('select amount from s_store where postID = 12345');
    if(result->amount < 0){
       throw new Exception('库存不足');
    }
}catch($e Exception){
    rollBack(回滚)
}
commit(提交事务)

先执行update锁住本条记录,这样就能保证其他线程执行不了更新操作,可以避免超扣现象。

    上述的方案的确解决了线程安全的问题,但是,别忘记,我们的场景是“高并发”。也就是说,会很多这样的修改请求,每个请求都需要等待“锁”,某些线程可能永远都没有机会抢到这个“锁”,这种请求就会死在那里。针对这个问题我们稍微修改一下上面的场景,我们直接将请求放入队列中的,采用FIFO(First Input First Output,先进先出),这样的话,我们就不会导致某些请求永远获取不到锁。下面是整个执行流程图:

09204745_rvB8.jpg

2 乐观锁解决方案

    悲观锁的解决方案解决了锁的问题,全部请求采用“先进先出”的队列方式来处理。那么新的问题来了,高并发的场景下,因为请求很多,系统处理队列内请求的速度根本无法和疯狂涌入队列中的数目相比,很可能一瞬间将队列内存“撑爆”,最终Web系统平均响应时候还是会大幅下降,系统还是陷入异常。

    这个时候,我们就可以讨论一下“乐观锁”的思路了。乐观锁,是相对于“悲观锁”采用更为宽松的加锁机制,大都是采用带版本号(Version)更新。实现就是,这个数据所有请求都有资格去修改,但会获得一个该数据的版本号,只有版本号符合的才能更新成功,其他的返回抢购失败。这样的话,我们就不需要考虑队列的问题,不过,它会增大CPU的计算开销。但是,综合来说,这是一个比较好的解决方案。

    乐观锁的执行流程如下:

09204745_e8dU.jpg

    有很多软件和服务都“乐观锁”功能的支持,例如Redis中的watch就是其中之一。通过这个实现,我们保证了数据的安全。

    下面是利用Redis中的watch实现乐观锁的代码

         while (true) {
            System.out.println(Thread.currentThread().getName());
            jedis = RedisUtil.getJedis();
            try {
                jedis.watch("mykey");
                int stock = Integer.parseInt(jedis.get("mykey"));
                if (stock > 0) {
                    Transaction transaction = jedis.multi();
                    transaction.set("mykey", String.valueOf(stock - 1));
                    List<Object> result = transaction.exec();
                    if (result == null || result.isEmpty()) {
                        // 可能是watch-key被外部修改,或者是数据操作被驳回
                        System.out.println("Transaction error...");
                    }
                } else {
                    System.out.println("库存为0");
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
                RedisUtil.returnResource(jedis);
            }finally{
                RedisUtil.returnResource(jedis);
            }

        }

 

 

参考资料:

http://www.cnblogs.com/php5/p/4362244.html 

http://www.kuqin.com/shuoit/20141203/343669.html

http://www.cnblogs.com/shihaiming/p/6062663.html

转载于:https://my.oschina.net/u/3145136/blog/855792

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值