美文网首页我爱编程
商品秒杀问题(悲观锁、乐观锁...)

商品秒杀问题(悲观锁、乐观锁...)

作者: 上善丨若水 | 来源:发表于2018-04-13 09:46 被阅读1426次

    一般在商城中,我们经常遇到某件商品只限10人抢购、秒杀
    if($num > 0){
    //用户抢购成功,记录用户信息
    $num--;
    }
    假设在一个并发量较高的场景,数据库中num的值为1时,可能同时会有多个进程读取到num为1,程序判断符合条件,抢购成功,num减一。这样会导致商品超发的情况,本来只有10件可以抢购的商品,可能会有超过10个人抢到,此时num在抢购完成之后为负值。

    我们该怎么解决呢?
    这里我们可以有两种方案,基于MySQL和redis的方案。

    基于MySQL(悲观锁和乐观锁)

    1、悲观锁
    悲观锁的方案采用的是排他读,也就是同时只能有一个进程读取到num的值。事务在提交或回滚之后,锁会释放,其他的进程才能读取(SELECT … FOR UPDATE)


    beiguan.png

    2、乐观锁
    乐观锁的方案在读取数据是并没有加排他锁,而是通过一个每次更新都会自增的version字段来解决,多个进程读取到相同num,然后都能更新成功的问题。在每个进程读取num的同时,也读取version的值,并且在更新num的同时也更新version,并在更新时加上对version的等值判断。假设有10个进程都读取到了num的值为1,version值为9,则这10个进程执行的更新语句都是UPDATE goods SET num=num-1,version=version+1 WHERE version=9,然而当其中一个进程执行成功之后,数据库中version的值就会变为10,剩余的9个进程都不会执行成功,这样保证了商品不会超发,num的值不会小于0,但这也导致了一个问题,那就是发出抢购请求较早的用户可能抢不到,反而被后来的请求抢到了。


    leguan.png

    基于redis解决方案

    1、基于watch的乐观锁方案
    watch用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。这种方案跟mysql中的乐观锁方案类似,具体表现也是一样的。


    watch.png

    2、基于list的队列方案
    基于队列的方案利用了redis出队操作的原子性,抢购开始之前首先将商品编号放入响应的队列中,在抢购时依次从队列中弹出操作,这样可以保证每个商品只能被一个进程获取并操作,不存在超发的情况。该方案的优点是理解和实现起来都比较简单,缺点是当商品数量较多是,需要将大量的数据存入到队列中,并且不同的商品需要存入到不同的消息队列中


    list.png

    相关文章

      网友评论

        本文标题:商品秒杀问题(悲观锁、乐观锁...)

        本文链接:https://www.haomeiwen.com/subject/vtnqkftx.html