mysql如何使用共享锁_mysql lock in share mode应用

1次阅读

必须用 lock in share mode 的场景是读取数据后需防止被修改但允许多个事务并发读,如“读-校验-后续可能更新”;它加共享锁,不阻塞其他共享锁,只阻塞排他锁。

mysql如何使用共享锁_mysql lock in share mode应用

什么时候必须用 LOCK IN SHARE MODE

当你需要读取一行数据、同时确保它不被其他事务修改(但允许别人也读),又不想升级成排他锁时,LOCK IN SHARE MODE 是唯一选择。典型场景是“读-校验-后续可能更新”,比如检查用户余额是否足够再扣款,但不立刻扣——这时不能让别人在你读完后立刻把钱转走。

  • 普通 select 不加锁,读到的是快照,无法阻止并发修改
  • SELECT ... for UPDATE 加的是排他锁,会阻塞其他所有读写,过度悲观
  • LOCK IN SHARE MODE 允许多个事务同时持有共享锁,只阻塞排他锁请求(如 UPDATEdeleteSELECT ... FOR UPDATE

LOCK IN SHARE MODE 的实际写法和常见错法

它必须出现在 SELECT 语句末尾,且只能用于已加索引的字段(否则会锁整张表)。最常被忽略的是:它不会自动提交,必须显式 COMMITROLLBACK 才能释放锁。

  • ✅ 正确:SELECT * FROM accounts WHERE id = 123 LOCK IN SHARE MODE;id 是主键)
  • ❌ 错误:SELECT * FROM accounts WHERE name = 'alice' LOCK IN SHARE MODE;name 无索引 → 行锁退化为表锁)
  • ❌ 错误:执行完就直接断开连接,没 COMMIT → 锁一直挂着,拖垮并发
  • ⚠️ 注意:在 READ COMMITTED 隔离级别下,共享锁在语句结束时就释放;REPEATABLE READ 下会持续到事务结束

SELECT ... FOR UPDATE 混用时的锁冲突表现

共享锁和排他锁互斥,但共享锁之间不互斥。这意味着两个事务可以同时对同一行执行 LOCK IN SHARE MODE,但只要有一个事务尝试 FOR UPDATE,就会被卡住,直到所有共享锁释放。

  • 事务 A:SELECT * FROM orders WHERE order_id = 456 LOCK IN SHARE MODE;
  • 事务 B:同样执行上面那条语句 → 成功,拿到共享锁
  • 事务 C:SELECT * FROM orders WHERE order_id = 456 FOR UPDATE; → 阻塞,等 A 和 B 都提交
  • 如果 A 或 B 在锁住期间执行 UPDATE → 触发锁升级失败,报错 Error 1205 (40001): Deadlock found

容易被绕过的坑:MVCC + 共享锁的边界

共享锁只锁“当前读”命中的行,不锁间隙(gap),也不影响幻读。如果你靠 LOCK IN SHARE MODE 想防止新记录插入导致的业务逻辑错乱,它做不到。

  • 例如:SELECT * FROM products WHERE category = 'book' LOCK IN SHARE MODE; → 只锁已有 book 记录,新插入一条 category='book' 的记录仍能成功
  • 想锁住范围?得用 SELECT ... FOR UPDATE 配合唯一索引或主键范围,或者在 REPEATABLE READ 下依赖间隙锁(但 LOCK IN SHARE MODE 不触发间隙锁)
  • 另一个隐形坑:mysql 8.0+ 对二级索引加共享锁时,会额外在聚簇索引上加隐式锁,排查死锁日志时容易漏看这一层

真正要用好 LOCK IN SHARE MODE,关键不是记住语法,而是清楚自己到底在防什么——是防别人改这一行?还是防别人插新行?防错对象,锁就白加了。

text=ZqhQzanResources