暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

select......for update会锁表还是锁行?

程序员恰恰 2024-03-03
16
select查询语句是不会加锁的,但是select .......for update
除了有查询的作用外,还会加锁,那么select......for update会锁表还是锁行?

验证:


创建表student,id是自增主键,给age 创建普通索引,phone创建唯一索引,字段name不加索引.
CREATE TABLE `student` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(100CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `age` int NOT NULL,
  `phone` varchar(11CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `index_phone` (`phone`)
ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4;

插入模拟数据:

INSERT INTO `student` (`id``name``age``phone`)
VALUES
 (1'22222'1'111'),
 (2'英语'2'1212'),
 (3'22222'3'2121'),
 (4'22222'4'21212'),
 (5'1111111'5'4444');

用到的索引相关命令

#查询表student的所有索引
show index from student
#id是自增主键
#age 创建普通索引
CREATE INDEX idx_age ON student (`age`);
#phone创建唯一索引
CREATE UNIQUE INDEX index_phone ON student(phone);

#name是普通字段
#age 创建普通索引,
#phone创建唯一索引

查看当前的索引情况:

需要关闭自动提交,通过set @@autocommit=0; 设置为手动提交。0代表手动提交,1代表自动提交。

实例1(主键):

会话1查询id=1的数据加了for update悲观锁,开启事务,并且没有提交

会话2去更新id为1的数据,被阻塞了

会话1,提交事务(释放锁),会话2立刻就会查询到数据。
证明:根据主键进行 for update 查询时是行锁
实例2(普通索引age):
给普通索引age添加悲观锁,会话1查询age=5的记录,开启事务并不提交。在会话2中更新age=5,发现sql
而在会话2中更新age=3的记录就不会
证明:根据普通索引进行 for update 查询时是行锁
实例3:(唯一索引)
给唯一索引phone添加悲观锁,会话1查询phone=1212的记录,开启事务并不提交。在会话2中更新phone=1212的记录,发现sql阻塞。
而在会话2中更phone=111的记录就不会
证明:根据唯一索引进行 for update 查询时是行锁
实例4:普通字段name
给普通字段name 添加悲观锁,会话1查询name=‘英语’的记录,开启事务并不提交。在会话2中更新name=‘英语的记录,发现sql阻塞。
会话2中更新name=‘22222的记录,发现sql同样会阻塞。
证明:根据普通字段进行 for update 查询时是表锁
结论:
如果查询条件用了索引(普通索引,唯一索引)/主键,那么select ..... for update就会进行行锁。如果是普通字段(普通索引,唯一索引/主键),那么select ..... for update就会进行锁表。
实战
如何在java代码中使用悲观锁?
例如,小明和小红同时购买了同一件商品,系统会生成两个订单。在执行减少库存的操作之前,系统会对该商品的库存进行加锁。这意味着只有一个用户能够成功地执行减少库存的操作,而另一个用户必须等待第一个用户完成操作并释放锁后才能执行。
通过使用悲观锁,我们可以有效避免多个用户同时购买同一件商品导致的库存冲突问题。悲观锁确保了数据的一致性,但也带来了一些性能上的损耗,因为其他用户必须等待锁的释放才能继续操作。

实现方式:
@Select("select * from tb_goods where goods_id = #{goodsId} for update")
public TbGoods lock(String goodsId);
业务代码:
@Service
public class OrderService {
    @Autowired
    private TbGoodsMapper goodsMapper;
    @Autowired
    private TbOrderMapper orderMapper;

    @Transactional
    public void buy(String goodsId){
        //获取数据库悲观锁(行锁)
        goodsMapper.lock(goodsId);

        //1:查询商品信息
        TbGoods tbGoods = goodsMapper.selectById(goodsId);
        if (tbGoods == null) {
            return;
        }
        //2:判断库存
        if(tbGoods.getGoodsStock1() <= 1){
            return;
        }
        //3:下单
        TbOrder tbOrder = new TbOrder();
        tbOrder.setOrderId(UUID.randomUUID().toString());
        tbOrder.setGoodsId(Integer.parseInt(goodsId));
        tbOrder.setOrderAmount(tbGoods.getGoodsPrice());
        orderMapper.insert(tbOrder);

        //4:修改库存
        tbGoods.setGoodsStock1(tbGoods.getGoodsStock1() - 1);
        goodsMapper.updateById(tbGoods);
    }
}

文章转载自程序员恰恰,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论