You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何锁定预订数据表?锁表后仍可读取的问题排查

为什么LOCK TABLE booking READ后其他用户仍能读取表?

你遇到的问题核心是对MySQL表级锁中**READ锁(共享读锁)**的特性理解有误,下面一步步拆解清楚:

1. READ锁的本质是「共享读,阻塞写」

当你执行LOCK TABLE booking READ时,这个锁的作用并不是禁止其他用户读取表,而是:

  • 持有该锁的会话只能对表执行读操作(如果尝试写会直接报错,你提到的插入操作其实应该无法执行,除非你中途解锁了?可能实际操作中存在误操作,但先回到锁的核心特性)
  • 其他所有会话依然可以正常读取该表,完全不受阻塞
  • 只有当其他会话尝试对该表执行**写操作(INSERT/UPDATE/DELETE)**时,才会被卡住,直到你释放READ锁

这就是为什么终端2的user2能顺利执行SELECT * from booking——共享读锁本来就允许并发读,它的设计目的是阻止写操作干扰读,而不是限制读本身。

2. 解决重复预订的正确锁方案

你的需求是避免用户重复预订同一时段,本质是要在自己校验、插入的过程中,阻止其他用户写入重叠的预订数据,这时候得用排他锁而非共享读锁:

方案1:表级WRITE锁(不推荐,并发极差)

如果一定要用表级锁,应该执行:

LOCK TABLE booking WRITE;

此时:

  • 持有锁的会话可以对表执行任意读写操作
  • 其他所有会话的读和写操作都会被阻塞,直到你执行UNLOCK TABLES;释放锁

但这种方式会彻底卡死其他用户的所有操作,并发性能拉胯,完全不适合实际业务场景。

方案2:行级排他锁(推荐,并发友好)

更合理的做法是用行级锁,通过带FOR UPDATE的查询锁定可能重叠的预订时段,流程如下:

-- 开启事务
START TRANSACTION;
-- 查询是否存在重叠的预订,同时给匹配行加排他锁
SELECT * FROM booking 
WHERE from_datetime < '2019-01-01 14:00:00' 
  AND to_datetime > '2019-01-01 12:00:00'
FOR UPDATE;
-- 如果上面的查询无结果,说明没有重叠,插入新预订
INSERT INTO booking (from_datetime, to_datetime) 
VALUES ('2019-01-01 12:00:00', '2019-01-01 14:00:00');
-- 提交事务,自动释放锁
COMMIT;

这种方式只会锁定与当前预订时段重叠的行(如果有的话),其他不相关的预订操作依然能正常执行,并发性能好很多。另外,给from_datetime和to_datetime创建联合索引,还能让FOR UPDATE的查询更快锁定目标行,避免意外的全表锁。

内容的提问来源于stack exchange,提问作者ruyawo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:39:12