InnoDB 和 MyISAM 有什么区别?
结论:InnoDB 是事务型引擎,聚簇索引、行级锁、MVCC、崩溃可恢复;MyISAM 是非事务引擎,索引与数据分离、只支持表级锁、崩溃后需要修复。MySQL 5.5 起 InnoDB 就是默认引擎,MyISAM 只在只读归档、全文检索的老系统里还有存量。
一、核心对照表
| 维度 | InnoDB | MyISAM |
|---|---|---|
| 事务 | 支持 ACID | 不支持 |
| 锁粒度 | 行级锁(默认),另有表级意向锁 | 表级锁(读共享、写排他) |
| 索引结构 | 聚簇索引,叶子存整行 | 非聚簇,叶子存行地址 |
| 外键 | 支持 | 不支持 |
| MVCC | 支持,读不加锁 | 不支持,读会加共享锁 |
| 崩溃恢复 | redo log 前滚 + undo 回滚 | 无,需 myisamchk 修复 |
| 计数 | COUNT(*) 需扫描 | 保存了行数,无 WHERE 时 O(1) |
| 全文索引 | 5.6 起支持 | 早期就支持 |
| 磁盘文件 | t.ibd(或共享表空间) | t.MYD 数据 + t.MYI 索引 |
| 并发写 | 行级,冲突小 | 表级,写阻塞全部读写 |
| 空间回收 | OPTIMIZE TABLE 重建 | OPTIMIZE TABLE 重建 |
二、文件与索引结构的差别
InnoDB
t.ibd ┌──────────────────────────────┐
│ 聚簇索引 B+Tree │
│ 叶子 = 主键 + 完整行数据 │
│ 二级索引 B+Tree │
│ 叶子 = 索引列 + 主键值 │
└──────────────────────────────┘
MyISAM
t.MYI ┌──────────────────────────────┐
│ 索引 B+Tree │
│ 叶子 = 索引列 + 行偏移量 │
└──────────────────────────────┘
t.MYD ┌──────────────────────────────┐
│ 堆表:行按插入顺序堆放 │
└──────────────────────────────┘
由此派生出两个直接后果:MyISAM 的二级索引查完还要按偏移量回数据文件再读一次,且行被删除后空洞不会自动复用(除非 OPTIMIZE);InnoDB 的主键查询一次 B+Tree 就拿到整行。
三、为什么默认引擎换成了 InnoDB
- 崩溃安全:MyISAM 写一半断电会留下损坏的表,只能
REPAIR TABLE;InnoDB 用 redo 重放已提交事务、undo 回滚未提交事务。 - 并发能力:MyISAM 的写锁是表级的,一个慢
UPDATE会挡住所有查询;InnoDB 行锁让不同行的写入互不干扰。 - 一致性读:MyISAM 的
SELECT会加共享锁,读写互相阻塞;InnoDB 靠 MVCC 提供快照读。 - 复制友好:MyISAM 表在 statement 格式下容易出现主从不一致(非事务语句部分生效),InnoDB 没有这个问题。
四、确认与迁移
-- 看实例支持哪些引擎
SHOW ENGINES;
-- 看某张表实际用的引擎
SELECT table_schema, table_name, engine, table_rows, data_length
FROM information_schema.tables
WHERE table_schema = 'demo' AND engine <> 'InnoDB';
-- 单表迁移(会重建表,期间只允许读,务必在低峰执行)
ALTER TABLE t_legacy ENGINE = InnoDB;
-- 迁移后确认
SHOW TABLE STATUS LIKE 't_legacy'\G
批量迁移建议用 pt-online-schema-change 或 gh-ost 做影子表拷贝,避免 ALTER 长时间持有元数据锁。迁移前先确认:表上没有 FULLTEXT 之外的 MyISAM 专有特性、没有依赖表级锁语义的业务逻辑、磁盘要预留约 2 倍空间。
五、备份与复制视角的差异
- 一致性备份:InnoDB 支持
--single-transaction拿到一致性快照(靠 MVCC),MyISAM 只能FLUSH TABLES WITH READ LOCK全库锁写。 - 崩溃后状态:InnoDB 重启自动前滚 + 回滚;MyISAM 需要人工
CHECK TABLE,否则可能带着损坏的索引继续服务。 - 主从复制:statement 格式下,MyISAM 语句中断会留下「部分生效」的表,从库无法自动纠偏;InnoDB 的事务边界让复制可以按事务重放。
- 表空间:MyISAM 单表文件便于直接拷贝搬迁,InnoDB 8.0 支持
TRANSPORTABLE TABLESPACE,但流程更重。
常见追问
- MyISAM 还有使用场景吗? 只读的日志归档、需要老式全文索引且不愿上 ES 的存量系统。新项目没有理由选它。
- InnoDB 的
COUNT(*)为什么要扫? 行数不是常量:MVCC 下不同事务看到的可见行不同,无法维护一个全局计数。 - MyISAM 的
COUNT(*)真的 O(1) 吗? 只有不带WHERE的全表计数走缓存的rows;带条件依旧要扫。 MEMORY引擎和 MyISAM 什么关系? 都用非聚簇的堆表结构,MEMORY数据在内存、重启即失,常用于临时表。- MyISAM 表损坏怎么修?
CHECK TABLE定位、REPAIR TABLE修复,严重时用myisamchk -r;先备份.MYD/.MYI。 - InnoDB 一定比 MyISAM 占空间大吗? 通常更大:聚簇索引冗余了整行、undo 与 redo 另占空间,但换来的是并发与安全。
小结
两者的差异是一整套设计取舍:事务与行锁换来更复杂的存储结构,非聚簇堆表换来简单与省空间。回答时抓住「事务、锁粒度、索引组织方式、崩溃恢复」四条主线,再补一句 MyISAM 计数快但仅限无条件的 COUNT(*),就足够完整。