Spring Boot 集成 MySQL 实现分布式锁
提示
本文中的完整代码已上传 Gitee:
# 简介
本项目会演示基于 MySQL 实现分布式锁的使用,会以常见的 “超卖”业务去演示
本项目中包含两种类型的锁
- 基于 MySQL 实现的乐观锁
- 基于 MySQL 实现的悲观锁
# 乐观锁
我认为我操作之前不会有人操作,如果有人操作了,那我再来一次,即为无锁操作
使用代码如下
@SneakyThrows
@Override
public String reduceStockOptimism(Integer id) {
ProductStock stock = productStockMapper.selectById(id);
if (stock != null && stock.getStock() > 0) {
int i = productStockMapper.reduceStockOptimism(id, stock.getVersion());
// i = 0 表示没有修改成功,说明有别人在你修改之前 已经修改了数据;需要重新再调用下当前方法
if (i == 0) {
// Thread.sleep 防止栈溢出
// Thread.sleep(10);
this.reduceStockOptimism(id);
}
} else {
throw new RuntimeException("库存不足!");
}
return "ok";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
productStockMapper.reduceStockOptimism
的 SQL 如下
<update id="reduceStockOptimism">
update product_stock
set stock = stock - 1,
version = version + 1
where id = #{id}
and version = #{version}
</update>
1
2
3
4
5
6
7
2
3
4
5
6
7
# 悲观锁
查询的时候,不管有没有线程竞争,都在 MySQL层面就加上了锁
使用代码如下
@Override
@Transactional(rollbackFor = Exception.class)
public String reduceStockGloomy(Integer id) {
// 加锁查询
ProductStock stock = productStockMapper.selectForLock(id);
if (stock != null && stock.getStock() > 0) {
productStockMapper.reduceStock(id);
} else {
throw new RuntimeException("库存不足!");
}
return "ok";
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
productStockMapper.selectForLock(id)
SQL 如下,借助于 for update
实现加锁
<select id="selectForLock" resultType="top.bulk.lock.mysql.entity.ProductStock">
select *
from product_stock
where id = #{id} for
update
</select>
1
2
3
4
5
6
2
3
4
5
6
# 本地测试说明
- 压测工具使用 JMeter , 当然也可以用别的,我用的这个, 此处不了解的可以参考 JMeter 相关操作
- 本地测试,借助于 IDEA 的
Allow parallel run
功能启动多个相同的服务(模拟线上环境多个副本),注意修改端口(-Dserver.port=8089),操作可以看下图 - 使用 Nginx 工具,将启动的多个项目做负载均衡; 此处不了解的可以参考 Nginx 配置负载均衡
- 接下来就是使用 JMeter 开启多个线程去压测 Nginx 暴露出来的接口了
上次更新: 2023/01/13, 18:09:17