MyISAM 一写就锁全表因其仅支持写优先的表级锁;InnoDB 行锁实际加在索引记录上,未走索引或非唯一索引会导致锁升级;死锁需通过 SHOW ENGINE INNODB STATUS 定位;多数场景应迁至 InnoDB,但需权衡空间与 COUNT(*) 性能。

MyISAM 为什么一写就锁全表?
因为 MyISAM 只支持 TABLE LOCK,且是「写优先」策略:只要有一个线程执行 INSERT、UPDATE 或 DELETE,整张表立刻进入写锁状态,其他所有读写请求全部排队等待。
常见错误现象:SHOW PROCESSLIST 里一堆 Locked 状态的查询;高并发写入时,连 SELECT COUNT(*) 都卡住。
- 使用场景仅适合「读多写少 + 数据量小 + 不要求事务」,比如日志归档表、配置快照表
-
READ LOCAL允许并发插入(concurrent insert),但前提是表尾没空洞(没删除过行),且只对INSERT有效,UPDATE/DELETE仍锁全表 - 不支持行级粒度,所以
WHERE条件再精准也没用,锁的是整张.MYI索引文件
InnoDB 行锁是不是真能锁单行?
不一定。InnoDB 的行锁实际是加在「索引记录」上的,不是加在数据行物理位置上。如果 WHERE 条件没走索引,或者走的是非唯一索引且结果集不止一行,就会升级为间隙锁(GAP LOCK)甚至临键锁(NEXT-KEY LOCK),导致看似无关的行也被堵住。
典型错误现象:UPDATE users SET status=1 WHERE name='alice' 卡住,但 name 字段没建索引 —— 实际锁了整个聚簇索引(即全表扫描范围)。
- 必须确认执行计划里
type是const、eq_ref或ref,否则大概率不是单行锁 - 唯一索引等值查询(如主键
id=123)才真正只锁那一行;非唯一索引会锁「值相同的所有行 + 间隙」 - 事务隔离级别影响锁行为:
READ COMMITTED下不加间隙锁,REPEATABLE READ默认开启,防幻读但扩大锁范围
死锁日志里看到 WAITING FOR THIS LOCK TO BE GRANTED 怎么快速定位?
MySQL 的 SHOW ENGINE INNODB STATUS 输出里,LATEST DETECTED DEADLOCK 段落才是关键。重点看两块:谁持有什么锁(HOLDS THE LOCK(S))、谁在等什么锁(WAITING FOR THIS LOCK TO BE GRANTED),再结合 SQL 和事务 ID 倒推执行顺序。
容易踩的坑:直接查 information_schema.INNODB_TRX 只能看到当前活跃事务,看不到已回滚的死锁参与者;而日志里显示的 TRANSACTION ID 是内部 ID,和 trx_id 不一致,不能和慢日志关联。
- 务必检查每条 SQL 是否命中索引,尤其是
UPDATE/DELETE的WHERE条件 - 多个更新语句尽量按相同顺序访问表和行(比如都先更新
orders再更新inventory),避免交叉加锁 - 应用层捕获
Deadlock found when trying to get lock错误后,应重试而非报错,InnoDB 已自动回滚其中一方
要不要把 MyISAM 表全换成 InnoDB?
绝大多数情况要换,但得看迁移代价。InnoDB 支持事务、行锁、外键、崩溃恢复,但磁盘占用通常比 MyISAM 高 20%–30%,且 COUNT(*) 全表统计更慢(需遍历索引树而非读元数据)。
真实兼容性问题:MyISAM 的 FULLTEXT 索引在旧版本 MySQL(INSERT DELAYED,InnoDB 完全不支持,得改逻辑。
- 换之前先用
pt-table-checksum校验数据一致性,别只靠mysqldump - 大表
ALTER TABLE ... ENGINE=InnoDB会锁表,建议用pt-online-schema-change或 MySQL 5.6+ 的ALGORITHM=INPLACE - MyISAM 的
auto_increment是表级缓存,重启后可能重复;InnoDB 是事务安全的,但若用innodb_autoinc_lock_mode=0(传统模式),高并发插入仍可能锁表
最常被忽略的一点:很多老系统依赖 MyISAM 的「崩溃后自动修复」机制(myisamchk),换成 InnoDB 后得靠 innodb_force_recovery 和备份恢复流程兜底,这部分预案往往没跟上。


















