MyISAM 为什么只支持表锁
因为 MyISAM 的设计从底层就放弃了行级并发控制能力。它没有事务日志、没有 MVCC 机制、也没有行锁所需的元数据结构(比如聚簇索引中的锁信息位或内存锁队列)。所有写操作(
INSERT、
UPDATE、
DELETE)都直接修改数据文件(
.MYD)和索引文件(
.MYI),必须靠整个表的互斥锁来保证一致性。
这导致一个
UPDATE即使只改一行,也会阻塞其他线程对同一张表的任何写入,甚至部分读操作(如
ALTER TABLE或某些
SELECT ... FOR UPDATE等价行为)。
MyISAM 表锁 vs InnoDB 行锁的实际表现差异
在高并发写场景下,MyISAM 的表锁会迅速成为瓶颈;而 InnoDB 的行锁只锁定被修改的记录(基于聚簇索引或二级索引加锁),其余行仍可并发读写。但要注意:InnoDB 的“行锁”不是绝对安全的——如果查询没走索引,会升级为表锁;而 MyISAM 压根不区分,一律表锁。
SELECT COUNT(*) FROM myisam_table不加锁,但会等正在执行的写锁释放
SELECT * FROM innodb_table WHERE id = 123可能只锁住这一行(若
id是主键)
SELECT * FROM innodb_table WHERE name = 'xxx'若
name没索引,会锁全表(
ALL扫描 + 意向锁升级)
MyISAM 表锁类型与隐式行为
MyISAM 使用两种表级锁:
READ锁(共享)和
WRITE锁(独占)。多个线程可以同时持有
READ锁,但只要有一个
WRITE锁在等待,后续所有
READ请求都会排队——这是典型的“写优先”策略,容易造成读饥饿。
更隐蔽的问题是:即使你只执行
SELECT,如果该语句触发了隐式锁(比如在
INSERT DELAYED或某些修复操作期间),也可能被挂起。另外,
REPAIR TABLE、
OPTIMIZE TABLE这类维护命令会强制获取
WRITE锁,且持续时间长,线上务必避开高峰。
切换存储引擎前必须验证的兼容点
把 MyISAM 表转成 InnoDB 并不只是执行
ALTER TABLE t ENGINE=InnoDB就完事。你要检查: 是否依赖
FULLTEXT索引?MyISAM 的全文索引语法和行为与 InnoDB 不完全一致(尤其分词和停用词) 是否有大量
INSERT ... SELECT或
LOAD DATA INFILE?InnoDB 默认开启事务,可能因 autocommit 关闭导致意外长事务 是否使用了
AUTO_INCREMENT并发插入优化?MyISAM 允许在表尾并发
INSERT,InnoDB 则需主键有序才能高效分配 ID 磁盘空间是否足够?InnoDB 数据文件通常比 MyISAM 大 20%~50%,尤其带大文本字段时
最常被忽略的是锁等待超时设置:
innodb_lock_wait_timeout默认 50 秒,而 MyISAM 根本不报锁等待错误——切过去后突然出现大量
Lock wait timeout exceeded,往往是因为应用没做重试或死锁处理。
