MySQL锁——一条更新语句处理并发

# MySQL更新语句编写

我们首先分析一下原有代码的处理逻辑:

//1,查询库存
            QueryWrapper<Stock> stockQueryWrapper = new QueryWrapper<>();
            stockQueryWrapper.eq("product_code", "1001");
            Stock stock = stockMapper.selectOne(stockQueryWrapper);
            //2.判断库存是否充足
            if (stock != null && stock.getCount() > 0) {
                stock.setCount(stock.getCount() - 1);
                //3.更新库存到数据库
                stockMapper.updateById(stock);
            }
1
2
3
4
5
6
7
8
9
10

我们可以使用一条更新的SQL语句,完成上述的三个步骤:

update db_stock set count = count - 1 where product_code = '1001' and count >=1;
1

这一条SQL语句是具备原子性的,因为update、delete、insert都是原子操作,都会加锁。

接下来我们在Dao层编写逻辑:

@Mapper
public interface StockMapper extends BaseMapper<Stock> {

    @Update("update db_stock set count = count - #{count} where product_code = #{productCode} and count >= #{count}")
    int updateStock(@Param("productCode") String productCode, @Param("count") Integer count);

}
1
2
3
4
5
6
7

最后我们的业务代码变为一行代码吧,并且需要把加锁的语句注释掉:

stockMapper.updateStock("1001",1);
1

我们还是在集群模式下进行压力测试,通过测试,我们查看数据库可以看出没有出现超卖现象,并发问题得以解决:

image-20221002233007613

# 总结

使用MySQL锁(一条更新语句)可以解决三种JVM锁失效的问题

# 一条MySQL语句的优缺点

# 优点

能够有效地解决多例模式、事务、集群下JVM锁失效的问题

# 缺点

锁范围:

  • 需要注意锁的范围

存在多个库存:

  • 同一条商品可能在其他地区也有仓库,一条SQL会出现问题,因为在实际情况中,我们会根据算法计算出仓库发货优先的顺序

无法记录库存变化的前后状态