巴中市网站建设_网站建设公司_关键词排名_seo优化
2026/1/16 16:31:43 网站建设 项目流程

视频看了几百小时还迷糊?关注我,几分钟让你秒懂!


很多开发者有个误区:

“只有写了SELECT ... FOR UPDATE才会加锁,普通的UPDATE不会加锁。”

这是完全错误的!

今天我们就用Spring Boot + MySQL(InnoDB)彻底讲清楚:

UPDATE语句天然就会加排他锁(X Lock)!
不需要你手动写FOR UPDATE


一、结论先行

SQL 语句是否加锁?加什么锁?
UPDATE ... WHERE id=1会加锁排他锁(X Lock)
DELETE ... WHERE id=1会加锁排他锁(X Lock)
SELECT ... 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?

如果没有锁,两个事务可能都基于stock=10计算,最终结果变成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 id=1
  • SELECT * FROM t WHERE id=1 FOR UPDATE

两者都会对id=1的行加排他锁(X Lock),行为一致。

💡 区别只在于:

  • UPDATE是“修改 + 加锁”
  • SELECT FOR UPDATE是“只加锁,不修改”(常用于后续业务判断)

五、锁的范围:只锁一行?还是更多?

这取决于WHERE 条件是否走索引

场景 1:通过主键/唯一索引更新(最常见)

UPDATE product SET stock=stock-1 WHERE id=1; -- id 是主键

只锁这一行(记录锁,Record Lock)


场景 2:通过非唯一索引更新

-- 假设 name 不是唯一索引 UPDATE product SET stock=stock-1 WHERE name='iPhone';

InnoDB 会:

  1. 找到所有name='iPhone'的行
  2. 每一行加排他锁
  3. 在 RR 隔离级别下,还会加间隙锁(Gap Lock),防止幻读!

⚠️ 如果没索引,就会锁全表(实际是逐行加锁,效果类似表锁)!


场景 3:无索引字段更新(灾难!)

-- age 无索引 UPDATE product SET stock=stock-1 WHERE age=18;

InnoDB 会:

  • 扫描全表
  • 对每一行都加排他锁(即使不满足条件,也会短暂加锁再释放)
  • 导致大量锁竞争,性能极差!

所以:UPDATE 的 WHERE 条件一定要走索引!


六、Spring Boot 中的正确用法

✅ 场景:安全扣库存(无需显式 FOR UPDATE)

@Service @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("库存不足或商品不存在"); } } }

Mapper:

@Update("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!


视频看了几百小时还迷糊?关注我,几分钟让你秒懂!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询