InnoDB 和 MyISAM 有什么区别?

#中间件#面试八股

InnoDB 和 MyISAM 有什么区别?

结论:InnoDB 是事务型引擎,聚簇索引、行级锁、MVCC、崩溃可恢复;MyISAM 是非事务引擎,索引与数据分离、只支持表级锁、崩溃后需要修复。MySQL 5.5 起 InnoDB 就是默认引擎,MyISAM 只在只读归档、全文检索的老系统里还有存量。

一、核心对照表

维度InnoDBMyISAM
事务支持 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(*),就足够完整。

笔记加载中…