• 第43月第23天 商品秒杀 乐观锁


    1.

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

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

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

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

    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,但这也导致了一个问题,那就是发出抢购请求较早的用户可能抢不到,反而被后来的请求抢到了。


    基于redis解决方案

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



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


    https://www.jianshu.com/p/aaa12e90e9b9
  • 相关阅读:
    MySql学习2
    Java学习:JDBC
    MySql学习
    Java学习:网络编程
    Java学习:反射
    Java学习:多线程(2)
    Java学习:多线程
    .net后台webclient用post方式发送文件和数据
    实用
    day&day
  • 原文地址:https://www.cnblogs.com/javastart/p/12758985.html
Copyright © 2020-2023  润新知