2026/5/21 11:40:35
网站建设
项目流程
长沙企业网站建设,网站平台建设招标书,长沙网站建站推广,上海房价2022年最新房价视频看了几百小时还迷糊#xff1f;关注我#xff0c;几分钟让你秒懂#xff01;很多开发者有个误区#xff1a;“只有写了 SELECT ... FOR UPDATE 才会加锁#xff0c;普通的 UPDATE 不会加锁。”这是完全错误的#xff01;今天我们就用 Spring Boot MySQL#xff08;…视频看了几百小时还迷糊关注我几分钟让你秒懂很多开发者有个误区“只有写了SELECT ... FOR UPDATE才会加锁普通的UPDATE不会加锁。”这是完全错误的今天我们就用Spring Boot MySQLInnoDB彻底讲清楚✅UPDATE语句天然就会加排他锁X Lock✅不需要你手动写FOR UPDATE一、结论先行SQL 语句是否加锁加什么锁UPDATE ... WHERE id1✅会加锁排他锁X LockDELETE ... WHERE id1✅会加锁排他锁X LockSELECT ... FOR UPDATE✅会加锁排他锁X Lock普通SELECT❌ 不加锁RR/RC 下快照读MVCC重点所有修改数据的语句UPDATE/DELETE/INSERT都会自动加排他锁二、为什么 UPDATE 会自动加锁 核心原因保证事务的隔离性和一致性假设没有锁-- 事务 A UPDATE product SET stock stock - 1 WHERE id 1; -- stock 从 10 → 9 -- 事务 B 同时执行 UPDATE product SET stock stock - 2 WHERE id 1; -- 它读到的 stock 还是 10如果没有锁两个事务可能都基于stock10计算最终结果变成8正确应为7——丢失更新Lost Update✅ 所以 InnoDB必须在UPDATE时对目标行加排他锁X Lock确保其他事务不能同时修改这行其他事务不能读取未提交的数据取决于隔离级别三、实战验证UPDATE 自动加锁步骤 1准备数据CREATE TABLE product ( id BIGINT PRIMARY KEY, stock INT NOT NULL ); INSERT INTO product (id, stock) VALUES (1, 10);步骤 2开启两个 MySQL 会话模拟两个事务会话 A先执行START TRANSACTION; UPDATE product SET stock stock - 1 WHERE id 1; -- 注意不提交此时stock在 A 中变为 9但未提交。会话 B后执行-- 尝试修改同一行 UPDATE product SET stock stock - 2 WHERE id 1;结果会话 B 被阻塞一直等待直到会话 A 提交或回滚。这说明UPDATE 自动加了排他锁B 必须等 A 释放锁才能继续。验证如果 A 提交-- 会话 A 执行 COMMIT;→ 会话 B 立刻继续执行最终stock 9 - 2 7正确四、UPDATE 加的是什么锁和 FOR UPDATE 一样吗✅ 完全一样UPDATE ... WHERE id1SELECT * FROM t WHERE id1 FOR UPDATE两者都会对id1的行加排他锁X Lock行为一致。 区别只在于UPDATE是“修改 加锁”SELECT FOR UPDATE是“只加锁不修改”常用于后续业务判断五、锁的范围只锁一行还是更多这取决于WHERE 条件是否走索引场景 1通过主键/唯一索引更新最常见UPDATE product SET stockstock-1 WHERE id1; -- id 是主键✅只锁这一行记录锁Record Lock场景 2通过非唯一索引更新-- 假设 name 不是唯一索引 UPDATE product SET stockstock-1 WHERE nameiPhone;InnoDB 会找到所有nameiPhone的行对每一行加排他锁在 RR 隔离级别下还会加间隙锁Gap Lock防止幻读⚠️ 如果没索引就会锁全表实际是逐行加锁效果类似表锁场景 3无索引字段更新灾难-- age 无索引 UPDATE product SET stockstock-1 WHERE age18;InnoDB 会扫描全表对每一行都加排他锁即使不满足条件也会短暂加锁再释放导致大量锁竞争性能极差✅所以UPDATE 的 WHERE 条件一定要走索引六、Spring Boot 中的正确用法✅ 场景安全扣库存无需显式 FOR UPDATEService Transactional public class ProductService { Autowired private ProductMapper productMapper; public void reduceStock(Long productId) { // 直接 UPDATE自动加锁 int updated productMapper.reduceStock(productId); if (updated 0) { throw new RuntimeException(库存不足或商品不存在); } } }MapperUpdate(UPDATE product SET stock stock - 1 WHERE id #{id} AND stock 0) int reduceStock(Param(id) Long id);✅ 优点利用数据库原子性自动加锁避免并发问题通过updated 0判断是否成功七、常见误区澄清❌ 误区 1“只有 SELECT FOR UPDATE 才加锁”→ 错所有写操作UPDATE/DELETE/INSERT都自动加排他锁。❌ 误区 2“UPDATE 不加锁靠 MVCC 解决并发”→ 错MVCC 只用于普通 SELECT快照读写操作必须加锁❌ 误区 3“加了索引就一定只锁一行”→ 不一定在RR 隔离级别 范围查询时会加间隙锁锁住一个范围。八、如何查看当前锁信息DBA 技巧-- 查看当前事务和锁 SELECT * FROM information_schema.INNODB_LOCKS; -- MySQL 5.7 SELECT * FROM performance_schema.data_locks; -- MySQL 8.0 -- 查看事务等待情况 SELECT * FROM information_schema.INNODB_LOCK_WAITS;九、总结问题答案UPDATE会自动加锁吗✅会加什么锁排他锁X Lock需要写FOR UPDATE吗❌不需要除非你只想锁但不改锁的范围由什么决定WHERE 条件 索引 隔离级别如何避免锁表确保 WHERE 条件走索引记住MySQL 的锁机制是自动的、智能的但前提是——你得用对索引、写对 SQL视频看了几百小时还迷糊关注我几分钟让你秒懂