mysql表级锁仅在myisam引擎或显式lock tables时生效;innodb默认行级锁,select/update/delete均不自动触发表锁,真需表锁只能用myisam或手动lock tables。

MySQL 表级锁只在 MyISAM 和显式 LOCK TABLES 时生效
InnoDB 默认用行级锁,SELECT、UPDATE、DELETE 都不会自动触发表锁——哪怕加了 WHERE 条件也没用。真要表级锁定,只有两种可靠路径:用 MyISAM 引擎,或手动执行 LOCK TABLES。别指望 SELECT ... FOR UPDATE 或事务隔离级别能锁整张表,它只锁命中行(甚至可能锁间隙)。
常见错误现象:SHOW PROCESSLIST 看到大量 Waiting for table level lock,基本说明你误用了 MyISAM,或者某段代码没释放 UNLOCK TABLES。
- MyISAM 表所有写操作(
INSERT/UPDATE/DELETE)自动加表写锁,读操作加表读锁;读写互斥,写写互斥 -
LOCK TABLES t1 READ, t2 WRITE后,当前会话只能读t1、读写t2,其他会话对这些表的写操作全被阻塞 - 锁不随事务提交/回滚释放,必须显式调用
UNLOCK TABLES,否则连接断开才自动释放
用 LOCK TABLES 做并发控制的实际限制
它不是并发安全的银弹,而是一个“粗粒度强制同步”工具,适合低频、短时、确定性流程(比如凌晨跑一次数据归档),不适合高并发业务逻辑。
使用场景举例:批量导入前清空临时表并禁止其他写入,或跨多表更新时防止中间态被读取。
- 锁是会话级的,不同连接之间无法感知彼此是否已加锁,容易漏掉
LOCK或重复LOCK - 一旦加了
WRITE锁,其他所有会话对该表的任何操作(包括SELECT)都会阻塞,超时由lock_wait_timeout控制(默认 31536000 秒) - 不能在存储过程中安全使用:如果过程异常退出,
UNLOCK TABLES不会自动执行,锁残留风险极高 - InnoDB 下加表锁后,再执行
INSERT INTO ... SELECT可能触发隐式行锁升级,导致死锁而非等待
替代方案:为什么多数时候该用 InnoDB 行锁 + 应用层协调
表锁太重,真正需要“避免并发冲突”的典型场景(如库存扣减、订单生成),靠表锁反而放大瓶颈。InnoDB 的 SELECT ... FOR UPDATE 或 INSERT ... ON DUPLICATE KEY UPDATE 配合唯一索引,才是更细、更稳的选择。
参数差异关键点:innodb_lock_wait_timeout 控制行锁等待上限(默认 50 秒),而表锁等待受 lock_wait_timeout 控制(默认 1 年),数值差异本身就说明设计意图不同。
- 扣库存示例:
UPDATE goods SET stock = stock - 1 WHERE id = 123 AND stock >= 1—— 利用原子条件更新,失败直接返回,不依赖锁 - 防重复下单:用
order_no做唯一索引,INSERT INTO orders (...) VALUES (...) ON DUPLICATE KEY UPDATE status = 'duplicate' - 真需要串行化处理(如计数器累加),优先考虑
UPDATE counter SET value = value + 1 WHERE id = 1,而不是先SELECT再UPDATE
MyISAM 表锁不可关闭,但可以绕过
如果你接手的旧系统用了 MyISAM,又不敢改引擎,那就得接受它的锁行为——没有配置项能关掉表锁,concurrent_insert 只影响 INSERT 是否允许并发追加,不解决更新冲突。
性能影响非常明显:100 个并发 UPDATE 请求,在 MyISAM 上是排队执行;在 InnoDB 上只要不碰同一行,基本可并行。
- 迁移建议:用
ALTER TABLE t ENGINE=InnoDB转换,注意检查外键、全文索引等兼容性(MyISAM 支持全文索引,InnoDB 5.6+ 才支持) - 转换后记得删掉所有
LOCK TABLES/UNLOCK TABLES逻辑,它们在 InnoDB 中意义不大,还可能干扰 MVCC - 如果必须保留 MyISAM(比如依赖其压缩表或全文检索),那就把写操作集中到低峰期,并监控
Table_locks_waited状态变量
最常被忽略的一点:LOCK TABLES 不会阻塞 DROP TABLE 或 TRUNCATE TABLE,后者会直接报错 ERROR 1051 (42S02): Unknown table 或 ERROR 1100 (HY000): Table 't' was not locked with LOCK TABLES —— 这种静默失败比阻塞更难排查。











