高 基础
InnoDB与MyISAM对比#
一句话答案#
InnoDB 支持事务/行锁/外键/MVCC/崩溃恢复(默认引擎),MyISAM 只支持表锁,适合读密集场景。
核心要点
InnoDB 使用 B+ 树作为索引结构。
B+ 树的核心特点:
[30 | 60] ← 非叶子节点(只存 key,不存数据)
/ | \
[10|20] [40|50] [70|80]
/ | \ / | \ / | \
叶子节点(存 key + 完整数据行 / 主键值)
[10]->[20]->[30]->[40]->[50]->[60]->[70]->[80]
↑ 叶子节点之间用双向链表相连plaintext关键特征:
- 非叶子节点只存 key,不存数据,路由用
- 叶子节点存 key + 数据(聚簇索引存完整行,二级索引存主键值)
- 叶子节点之间用双向链表连接,支持高效范围查询
- 所有数据都在叶子节点,查询路径长度固定(稳定的 O(log N))
两者 B+ 树叶子节点的本质差异(高频深挖):
| 维度 | MyISAM(非聚簇) | InnoDB(聚簇) |
|---|---|---|
| 主键索引叶子存什么 | 行数据的物理地址(指向 .MYD 数据文件的偏移) | 完整行数据(数据就在主键 B+ 树叶子里) |
| 二级索引叶子存什么 | 行数据的物理地址(同主键索引) | 主键值(不是地址!) |
| 主键 vs 二级索引结构 | 对称——都是”索引值 → 物理地址”,没有谁是”主” | 不对称——主键聚簇、二级索引非聚簇 |
| 数据与索引文件 | 分离:.MYI 存索引、.MYD 存数据 | 一体:数据即索引(聚簇),存在 .ibd |
| 查二级索引 | 索引叶子直接拿到物理地址 → 一次定位拿到行 | 索引叶子拿到主键 → 回表再查一次主键 B+ 树 |
为什么 InnoDB 二级索引叶子存”主键”而不是”物理地址”?
关键在于 InnoDB 是聚簇索引、且页可能分裂/合并。插入新行导致页满时会发生页分裂,已有行的物理位置会移动。如果二级索引存物理地址,每次页分裂就要回头更新所有二级索引里的地址,代价巨大。改存主键值——主键值不随物理位置变化而变化,行搬到哪都不影响二级索引,只是查询时多一次按主键回表的成本。
MyISAM 没有聚簇概念,数据行追加写入 .MYD、位置固定不变(删除留空洞也不移动其它行),所以叶子直接存物理地址既快又安全,主键和二级索引也就能用同一套”指向地址”的对称结构。
面试回答(2分钟版)
InnoDB 和 MyISAM 最核心的区别在于事务和锁。InnoDB 支持 ACID 事务、行级锁、MVCC 多版本并发控制和崩溃恢复,是 MySQL 5.5 之后的默认引擎;MyISAM 不支持事务,只有表级锁,并发写性能很差。索引结构上两者都用 B+ 树,但 InnoDB 的主键索引是聚簇索引,叶子节点存完整数据行,二级索引叶子存主键值需要回表;MyISAM 的索引和数据是分离的,叶子节点存的是数据文件的物理地址。MyISAM 有个优势是 count() 不带 WHERE 时可以直接返回存储的行数常量,但 InnoDB 因为 MVCC 的可见性需要逐行判断所以 count() 更慢。现在几乎所有业务场景都选 InnoDB,MyISAM 只在极少数只读且需要全文检索的历史场景才会碰到。
追问与易错
追问方向:
- “现在为什么几乎不用 MyISAM?”→ 不支持事务和行锁,并发写性能极差;不支持崩溃恢复,宕机后数据易损坏;InnoDB 5.6+ 已支持全文索引,MyISAM 仅存优势基本消失
- “MyISAM count(*) 为什么快?”→ MyISAM 内部维护了一个行数计数器,不带 WHERE 的 count(*) 直接返回该常量值,时间复杂度 O(1)
- “InnoDB count(*) 怎么优化?”→ InnoDB 因 MVCC 需逐行判断可见性无法存计数器;优化方案:用最小的二级索引扫描减少 IO、维护 Redis/计数表缓存行数、或用 EXPLAIN 的 rows 做近似估算
- “为什么 InnoDB 二级索引叶子存主键而不是行地址?”→ InnoDB 聚簇且页会分裂/合并,行的物理位置会移动;若二级索引存物理地址,每次页分裂都要更新所有二级索引的地址,代价极大。存主键值则行搬到哪都不影响二级索引,代价只是查询时多一次按主键回表。MyISAM 数据位置固定不变,所以叶子直接存物理地址,主键与二级索引结构对称
易错点:
- ❌ MyISAM 没有任何优势——全文检索场景可用
- ❌ InnoDB 不支持全文索引——5.6+ 已支持