数据库中的超卖现象
早睡蛋
# 改造代码 引入数据库信息
编写数据库库存信息
编写实体类
@Data
@TableName("db_stock")
public class Stock {
/**
* 主键
*/
private Long id;
/**
* 商品编号
*/
private String productCode;
/**
* 仓库
*/
private String warehouse;
/**
* 库存量
*/
private Integer count;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
使用MyBatis-Plus并且编写对应的Mapper、Service、Controller层
在业务层编写减库存逻辑:
@Autowired
private StockMapper stockMapper;
public void deduct() {
// lock.lock();
try {
QueryWrapper<Stock> stockQueryWrapper = new QueryWrapper<>();
stockQueryWrapper.eq("product_code", "1001");
Stock stock = stockMapper.selectOne(stockQueryWrapper);
if (stock != null && stock.getCount() > 0) {
stock.setCount(stock.getCount() - 1);
stockMapper.updateById(stock);
}
} finally {
// lock.unlock();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
进行压力测试,结果为:
此时我们发现当牵扯到数据库业务时,存在严重的并发问题
# 分析
理论上,库存剩余量为0到4950,因为在压力测试中,我设置了50个请求,每一个请求同时有100个线程并发进行,所以最坏的情况就是每一次请求只有一个请求减库存是正确的。
# 使用本地锁解决数据库超卖问题
我们使用之前的本地锁进行优化:
private ReentrantLock lock = new ReentrantLock();
@Autowired
private StockMapper stockMapper;
public void deduct() {
lock.lock();
try {
QueryWrapper<Stock> stockQueryWrapper = new QueryWrapper<>();
stockQueryWrapper.eq("product_code", "1001");
Stock stock = stockMapper.selectOne(stockQueryWrapper);
if (stock != null && stock.getCount() > 0) {
stock.setCount(stock.getCount() - 1);
stockMapper.updateById(stock);
}
} finally {
lock.unlock();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
通过压力测试,我们可以顺利地解决并发问题
另外,使用synchronized也是没有问题的。