商品秒杀业务场景使用Mysql的解决思路|悲观锁、乐观锁实例与curl多线程请求

2019年7月30日 0 条评论 1.99k 次阅读 0 人点赞

本实例使用到了curl模拟并发操作与悲观锁和乐观锁的方法

在分享本实例前让我们先引入以下场景

数据库中有两张表,一张为秒杀活动的商品库存表,一张为用户订单表,此次秒杀的商品为iPhone X手机,库存为10部, 在一个并发量的秒杀场景中,当数据库中库存值为1时,可能同时会有多个进程读取到库存数的值,他们在同一时间点读到的库存值都为1,程序判断符合条件,抢购成功,库存数减一。这样会导致商品超发的情况,本来只有10件可以抢购的商品,可能会有超过10个人抢到,此时库存在抢购完成之后为负值。

在秒杀前,数据表示例如下

秒杀后的库存结果如下所示,并且此时有16人秒杀到商品

很明显,当库存秒杀到0时,照常理说,是停止秒杀活动,但是在此处,它直接将库存数量降低为负数,那么为什么会产生这种情况呢?让我们看看以下的错误的代码示例

假设单位时间t1内, 商品库存数量为1时,此时有7个用户同时访问,他们都读到了库存数量等于1,都执行了更新库存操作.使库存数量-1,所以就造成了这种结果。

那么如何解决这个问题呢?其实方法有很多,以mysql为例我们可以为此数据表或者记录上锁。

解决方案一:悲观锁

悲观锁的方案采用的是排他读,也就是同时只能有一个进程读取到库存的值。事务在提交或回滚之后,锁会释放,其他的进程才能读取。该方案最简单易懂,在对性能要求不高时,可以直接采用该方案。要注意的是,SELECT … FOR UPDATE要尽可能的使用索引,以便锁定尽可能少的行数;排他锁是在事务执行结束之后才释放的,不是读取完成之后就释放,因此使用的事务应该尽可能的早些提交或回滚,以便早些释放排它锁。

具体实现代码如下

 

悲观锁简单的理解就是给一条记录加上行级锁,当用户秒杀商品时,对该记录进行锁定,当一个用户执行完抢购秒杀活动时,下一个用户才能进来,否则只能进行等待。它的缺点显而易见,如果在大并发下,用户体验会很差,因为该记录在执行的情况下,很多用户都要进行阻塞等待。

解决方案一:乐观锁

乐观锁的方案在读取数据是并没有加锁,而是通过一个每次更新都会自增的version字段来解决多个进程读取到相同库存,然后都能更新成功的问题。在每个进程读取库存的同时,也读取version的值,并且在更新库存的同时也更新version,并在更新时加上对version的等值判断。假设有10个进程都读取到了库存的值为1,version值为9,则这10个进程执行的更新语句都如下:

UPDATE goods SET storage=storage-1,version=version+1 WHERE version=9

然而当其中一个进程执行成功之后,数据库中version的值就会变为10,剩余的9个进程都不会执行成功,这样保证了商品不会超发,并且库存的值不会小于0,但这也导致了一个问题,那就是发出抢购请求较早的用户可能抢不到,反而被后来的请求抢到了。

数据表设计如下所示

具体代码如下所示:

在此,我们就对乐观锁以及悲观锁的实现先告一段落。

接下来就是模拟curl多线程并发的操作

具体代码如下:

 

 

本实例就是使用这个curl多线程来模拟本地的并发操作的

代码中$num这个变量看情况设置大小 ,因为设置太大,可能本地服务器环境吃不消

还有些redis的秒杀队列的方法暂时就不在此分享了,具体解决方案请查看该链接

链接地址:https://www.jb51.net/article/165938.htm

兰陵美酒郁金香

大道至简 Simplicity is the ultimate form of sophistication.

文章评论(0)

你必须 登录 才能发表评论