myisam索引叶子节点存物理地址,innodb聚簇索引叶子节点存整行数据;myisam回表为一次固定i/o,innodb二级索引回表需二次b+树查找,代价更高。

INFORMATION_SCHEMA 里查不到索引物理结构,得靠理解底层逻辑来判断行为差异
MyISAM索引叶子节点存的是物理地址
MyISAM 的 .MYI 文件里,不管是主键还是二级索引,B+树叶子节点都只存数据在 .MYD 文件里的偏移量(比如第 127 行、或文件字节偏移 8192)。查询时必须先走索引树拿到地址,再跳转到数据文件读记录——这叫“回表”,但回表开销是固定的一次磁盘寻址。
常见错误现象:SELECT * FROM myisam_table WHERE name = 'xxx' 即使 name 有索引,仍要额外一次 I/O 去 .MYD 读完整行。如果只查 name 和 id,而这两个字段又都在索引里,MyISAM 依然无法避免回表——它不支持覆盖索引优化。
- MyISAM 允许表没有主键,所有索引地位平等
- 索引文件和数据文件完全解耦,
OPTIMIZE TABLE是为合并碎片、重排.MYD中的物理行 - 并发写入时表级锁会卡住所有基于该索引的查询,哪怕只是查不同 key
InnoDB主键索引叶子节点直接存整行数据
InnoDB 的聚簇索引意味着:主键索引的 B+树叶子节点不是指针,而是真实的数据页(16KB),里面就放着整行记录。所以 SELECT * FROM innodb_table WHERE id = 123 查主键,一次索引定位就拿到全部字段,不用额外跳转。
但二级索引就不同了:叶子节点只存对应记录的主键值(比如 id),查 name 索引时,先定位到 name='xxx' 的叶子项,取出它的 id,再拿着这个 id 去主键索引树里再查一遍——这就是“回表”。
- 没显式定义主键?InnoDB 会悄悄建个隐藏
ROW_ID当聚簇索引,但这个 ID 不暴露给 SQL,也不可用于ORDER BY - 主键越短越好,因为所有二级索引叶子都要存它;用
VARCHAR(255)当主键会让二级索引体积暴增 -
SELECT name, id FROM innodb_table WHERE name = 'xxx'可以走覆盖索引——只要name索引包含id字段(即INDEX(name, id)),就不用回表
为什么 COUNT(*) 在 MyISAM 快,在 InnoDB 慢
MyISAM 在表的元信息里缓存了总行数,COUNT(*) 直接返回这个值,不扫表。InnoDB 没这个缓存,因为它支持 MVCC 和事务:同一时刻不同事务看到的行数可能不同,必须现场统计。
但注意:如果加了 WHERE 条件,比如 COUNT(*) WHERE status = 1,两者都得走索引或全表扫描,性能差距就没了。
- MyISAM 的
COUNT(*)结果可能不准——崩溃后没修复,计数就错位了 - InnoDB 的
COUNT(*)在大表上慢,不是因为算法差,而是它得确保结果对当前事务隔离级别有效 - 想加速 InnoDB 的行数统计?加一个冗余字段维护计数,或用近似值
SHOW TABLE STATUS LIKE 't'查Rows字段(误差可能达 40%)
真正容易被忽略的点是:索引结构差异直接影响 SQL 写法。比如 MyISAM 上加个 name 索引,对 SELECT name, email 没帮助;而 InnoDB 上建 INDEX(name, email) 就能覆盖查询。这不是配置问题,是存储引擎底层决定的。











