InnoDB支持行级锁而MyISAM仅支持表级锁;行锁实际作用于索引记录,无索引时会退化为表锁;死锁自动检测并回滚,需通过SHOW ENGINE INNODB STATUS或开启innodb_print_all_deadlocks分析。

行级锁只在 InnoDB 有效,MyISAM 根本不支持
MySQL 的锁机制和存储引擎强绑定。InnoDB 支持行级锁(SELECT ... FOR UPDATE、UPDATE、DELETE 等语句默认加行锁),而 MyISAM 只支持表级锁(所有 DML 操作都锁整张表)。如果你执行了 SELECT ... FOR UPDATE 却没生效——先查 SHOW CREATE TABLE tbl_name,确认引擎是不是 ENGINE=InnoDB。
常见错误现象:
- 在 MyISAM 表上执行
SELECT ... FOR UPDATE不报错,但实际无锁效果(语句成功,锁未建立) - 事务中更新某几行后,其他事务仍能修改同一张 MyISAM 表的任意行(因为根本没行锁)
- 误以为加了索引就自动用行锁——其实 InnoDB 行锁是加在索引记录上的,若
WHERE条件无法命中索引,会退化为表级锁(锁全表所有索引项,等效于锁表)
行锁不是“锁住某行数据”,而是锁住索引记录
InnoDB 的行级锁本质是锁住聚簇索引或二级索引中的记录。这意味着:
-
UPDATE users SET name='a' WHERE id = 100:如果id是主键,则锁住聚簇索引中id=100的那条记录 -
UPDATE users SET name='a' WHERE email='x@y.z':如果email有唯一索引,则锁住该二级索引记录 + 对应的聚簇索引记录(next-key lock) -
UPDATE users SET name='a' WHERE status = 1:若status无索引,InnoDB 会扫描全表,对每条匹配记录加锁,同时可能触发间隙锁(gap lock)或临键锁(next-key lock),导致大量无关行被锁定
容易踩的坑:用 EXPLAIN 看执行计划,确认 type 是否为 const/ref;如果是 ALL 或 index,大概率已丧失行锁粒度。
表级锁不只是 LOCK TABLES,隐式锁更常见
显式表锁(LOCK TABLES t1 WRITE)极少在业务代码中使用,但隐式表级锁频繁出现:
-
ALTER TABLE在大多数 MySQL 版本中会获取元数据锁(MDL),阻塞后续 DML,本质是表级协调锁 - MyISAM 执行
INSERT时自动加表写锁,此时任何读(SELECT)都会等待 - InnoDB 在某些 DDL 场景下(如无
ALGORITHM=INPLACE的列添加)也会升级为表级锁 -
FLUSH TABLES WITH READ LOCK是全局表级读锁,常用于物理备份,但会阻塞所有写入
性能影响明显:一个慢 ALTER TABLE 可能让整个库的写请求排队,监控时注意 show processlist 中状态为 Waiting for table metadata lock 的线程。
死锁检测与日志怎么看
InnoDB 死锁检测是自动开启的(innodb_deadlock_detect=ON),一旦发生,MySQL 会选一个事务回滚并返回错误:
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
要定位原因,必须开死锁日志:
- 设置
innodb_print_all_deadlocks = ON(写入 error log,非实时输出) - 或执行
SHOW ENGINE INNODB STATUS\G,查看LATEST DETECTED DEADLOCK区域
日志里关键信息包括:
- 每个事务持有的锁(
HELD LOCKS) - 正在等待的锁(
WAITING FOR THIS LOCK TO BE GRANTED) - 涉及的表名、索引名、具体记录的主键值(如
space id 123 page no 1024 n bits 72后跟record lock, heap no 5)
真正难处理的是“锁等待链过长”而非死锁:比如事务 A 锁住行 X,事务 B 等 A;B 又锁住行 Y,事务 C 等 B……这种不会触发死锁检测,但会导致请求堆积,需靠 information_schema.INNODB_TRX 和 INNODB_LOCK_WAITS 关联分析。










