SpringBoot抢购商品,悲观锁、乐观锁

文章目录

  • 搭建SpringBoot项目
  • AB进行并发测试
  • 悲观锁
  • 乐观锁
    • 加时间戳的乐观锁
    • 使用限定次数重入的乐观锁

搭建SpringBoot项目

这个小项目的源码放到了GitHub上,可供参考 项目源码

和传统的web项目一样,分成了controller、service、mapper、dao四层,使用了mybatis来连接数据库

项目中的多次更改均采用注释,所以代码看起来有些乱

最后还可以用redis来做处理并发问题,这个还在学习,希望有机会学明白后可以补上

AB进行并发测试

参考的这篇文章:Apache Bench。这个老哥里面的安装过程真实可靠。

开始报这个错没关系,只要访问http://localhost/可以成功,就是ok的
在这里插入图片描述
需要注意的部分:

  • 因为带参数的请求不会弄,所以代码里的请求都改成了Get请求。每次请求的参数都是写死的
  • C:\Apache24\bin>ab.exe -n 2000 -c 50 http://localhost:8080/purchase,这个请求后面不需要加 \path

不加锁的测试结果:
SpringBoot抢购商品,悲观锁、乐观锁_第1张图片
可以看到花费的时间是:1.67s
因为每次请求都是减去10个库存,数据库初始值是10000,所以最多有1000次请求成功
但是图中显示只有994次失败,所以现在数据库的库存应该为-60
查看数据库发现结果也是这样的
在这里插入图片描述

悲观锁

使用悲观锁只需要在查询语句后加 for update,这样就锁定查询出的数据,其他事务不能再进行读写,这样来避免数据的不一致性。

<select id="getProduct" parameterType="long" resultType="org.hg.demo.pojo.ProductPo">
	select id, product_name as productName, stock, price, version from product
	where id=#{id} for update
select>

加悲观锁的测试结果:
SpringBoot抢购商品,悲观锁、乐观锁_第2张图片
花费时间增加了0.5s左右
有一千次成功,一千次失败。所以没有出现并发的数据问题,数据库的剩余库存为0。
在这里插入图片描述

乐观锁

乐观锁不会对数据真正加锁,而是通过版本号机制来保证数据一致性
每次进行数据库的更新时,都会检查数据的版本号是不是和自己查询到的一样,只有一样的话才会进行更新。如果不一样,就说明数据在这个期间被更改过,就回退,不再进行操作。


<update id="decreaseProduct">
	update product set stock  = stock - #{quantity}, version = version + 1
	where id = #{id} and version=#{version}
update>

乐观锁的测试结果:
SpringBoot抢购商品,悲观锁、乐观锁_第3张图片
这个测试结果出乎意料,竟然比不加锁还快
看到数据就知道了原因,只有200多次请求成功了,其他的请求都因为数据不一致而回退。
这次耗时更少的原因应该就是比较操作比数据库的插入操作更减少时间。
数据库的结果:
在这里插入图片描述

加时间戳的乐观锁

计算开始时间和现在时间,如果两者之差小于某个时间段,则再次进行尝试进行操作。
测试结果如下,和期望也是不大一样,只有耗时增加了,但是成功率基本没变,甚至还减少了。
SpringBoot抢购商品,悲观锁、乐观锁_第4张图片

使用限定次数重入的乐观锁

每个请求如果不成功的话,总共可以尝试三次。
测试结果如下,时间依旧是增加的,但是没有上一次那么厉害。
失败次数也是很多的,感觉这个和上一个时间戳基本没起作用,而且还浪费了大量时间,可能是因为业务的原因导致了可重入锁产生了大量冲突。
SpringBoot抢购商品,悲观锁、乐观锁_第5张图片

你可能感兴趣的:(并发编程,SpringBoot)