外观
for share 和 for update 有什么区别?
⭐ 题目日期:
字节 - 2024/12/17
📝 题解:
在 MySQL 中,FOR SHARE
和 FOR UPDATE
是两种显式加锁的语句,用于控制事务并发访问数据时的行为。它们的核心区别在于 锁类型 和 使用场景,以下是详细对比:
1. 锁类型与兼容性
语句 | 锁类型 | 锁兼容性 |
---|---|---|
FOR SHARE | 共享锁(S Lock) | - 允许其他事务加 FOR SHARE 。- 禁止其他事务加 FOR UPDATE 或修改数据。 |
FOR UPDATE | 排他锁(X Lock) | - 禁止其他事务加任何锁(包括 FOR SHARE 和 FOR UPDATE )或修改数据。 |
2. 核心行为对比
特性 | FOR SHARE | FOR UPDATE |
---|---|---|
读操作 | 允许其他事务读 | 允许其他事务读(但部分场景可能被阻塞) |
写操作 | 禁止其他事务修改数据 | 禁止其他事务修改数据 |
锁范围 | 共享锁(Next-Key Lock 或行锁) | 排他锁(Next-Key Lock 或行锁) |
并发性能 | 高(允许多事务同时读) | 低(完全独占资源) |
典型场景 | 需要读取数据并防止被修改(如生成报表) | 需要修改数据(如更新订单状态) |
3. 使用场景示例
场景 1:数据读取保护(FOR SHARE)
- 需求:事务 A 需要读取用户余额,并确保在事务提交前其他事务不能修改该余额。
- 操作:
BEGIN; -- 加共享锁,允许其他事务读,但禁止修改 SELECT balance FROM accounts WHERE user_id = 1 FOR SHARE; -- 其他事务可以执行 SELECT ... FOR SHARE,但不能执行 UPDATE 或 SELECT ... FOR UPDATE COMMIT;
场景 2:数据修改保护(FOR UPDATE)
- 需求:事务 A 需要扣减库存,并确保在事务提交前其他事务不能修改库存。
- 操作:
BEGIN; -- 加排他锁,禁止其他事务读写 SELECT stock FROM products WHERE id = 100 FOR UPDATE; UPDATE products SET stock = stock - 1 WHERE id = 100; COMMIT;
4. 锁的底层行为
4.1 锁的粒度
- 默认锁类型:Next-Key Lock(行锁 + 间隙锁),防止幻读。
- 退化为行锁:如果查询条件命中唯一索引且精确匹配单行,退化为行锁(无间隙锁)。
- 退化为表锁:如果无索引,可能锁全表(性能极差)。
4.2 锁的释放时机
- 两种锁均在事务提交或回滚时释放。
5. 注意事项
5.1 死锁风险
- FOR SHARE:多个事务同时持有共享锁,若某个事务尝试升级为排他锁(如
UPDATE
),可能死锁。 - FOR UPDATE:多个事务按不同顺序加排他锁可能死锁。
- 解决方案:统一加锁顺序,或设置合理的超时时间(
innodb_lock_wait_timeout
)。
5.2 性能影响
- FOR SHARE:适合读多写少场景,允许高并发读。
- FOR UPDATE:写操作频繁时易引发锁竞争,需谨慎使用。
6. 总结
维度 | FOR SHARE | FOR UPDATE |
---|---|---|
锁类型 | 共享锁(S Lock) | 排他锁(X Lock) |
读兼容性 | 允许其他事务读 | 允许其他事务读(但可能被阻塞) |
写兼容性 | 禁止其他事务写 | 禁止其他事务写 |
使用场景 | 保护数据读取一致性 | 保护数据修改原子性 |
性能影响 | 高并发读 | 低并发,高独占性 |
关键结论:
- 使用
FOR SHARE
保护 只读操作 的共享访问,适用于读多写少场景。 - 使用
FOR UPDATE
保护 写操作 的独占性,适用于数据强一致性要求的修改场景。 - 合理选择锁类型,避免过度加锁导致性能下降或死锁。