MySQL中MyISAM的索引文件和数据文件为什么是分离存储的?

作者:袖梨 2026-07-12
MyISAM索引与数据分离是刻意设计:.MYI存B+Tree索引(叶子存磁盘地址),.MYD为纯堆表;查询需两次I/O,全表扫描快;不支持事务、行锁、崩溃恢复,故无需聚簇;写多并发高时性能下降,且不支持覆盖索引。

MyISAM 的索引和数据分离,不是设计缺陷,而是刻意为之的轻量级取舍:它压根不打算支持事务、行锁或崩溃恢复,所以不需要把数据和索引绑死在一块。

MyISAM 的 .MYI 和 .MYD 各自只干一件事

这种分离直接体现在文件职责上:

  • .MYI 文件只存 B+Tree 索引结构,叶子节点里塞的是地址(比如 0x1A2B3C 这样的磁盘偏移量),不是数据本身
  • .MYD 文件就是个纯堆表,按插入顺序线性写入,不排序、不聚簇、不维护逻辑顺序
  • SELECT * FROM t WHERE id = 123 时,先读 .MYI 找到地址,再跳转去 .MYD 读那一行 —— 两次 I/O
  • SELECT * FROM t 全表扫描时,完全绕过 .MYI,直接顺序读 .MYD,速度极快

为什么不像 InnoDB 那样把数据塞进叶子节点?

InnoDB 必须聚簇,是因为它要靠主键组织物理存储来支撑 MVCC、行锁、崩溃恢复。MyISAM 没这些需求:

  • 不支持事务 → 不需要 undo log / redo log,也不依赖“主键 ⇔ 物理位置”强绑定
  • 只支持表锁 → 加锁对象是整个 .MYD 文件,无需按主键精确定位某一行
  • 崩溃后无法安全恢复 → .MYI 损坏就只能 REPAIR TABLE,没机制重建地址映射
  • 所以它选择最简路径:索引归索引,数据归数据,各自独立更新,逻辑清晰、实现简单

分离带来的实际代价常被低估

写多或并发高时,问题立刻暴露:

  • 每次 UPDATEDELETE 都得同时改 .MYI.MYD,两个文件可能落在不同磁盘块,随机 I/O 增加
  • INSERT 会锁住整个 .MYD,其他读写全部阻塞
  • OPTIMIZE TABLE 实际是重建两个文件,期间表不可写,且临时空间暴涨
  • 真正容易被忽略的是:MyISAM 不支持覆盖索引优化。哪怕 SELECT name FROM t WHERE email = 'x@y',且 (email, name) 有联合索引,它仍要回 .MYD 查 —— 因为索引叶子节点里只有地址,不存任何列值

这个“地址即一切”的模型,决定了 MyISAM 的能力边界:适合读多写少、可接受表锁、能容忍修复失败的场景。一旦写入压力上来,或者需要强一致性,分离结构就成了硬伤,而不是特色。

相关文章

精彩推荐