MySQL为何不推荐用SELECT *,尤其在覆盖索引和IO优化场景下?
- 内容介绍
- 文章标签
- 相关推荐
本文共计975个文字,预计阅读时间需要4分钟。
直接使用SELECT *会破坏覆盖索引,强制回表查询,这并非可能慢,而是必然慢,因为它是完全随机+I/O的过程。
为什么 SELECT * 必然让覆盖索引失效
覆盖索引生效的前提是:查询所需所有字段都完整存在于某个二级索引的叶子节点中。一旦用了 SELECT *,MySQL 就必须把整行数据捞出来——哪怕你只差一个 TEXT 或 BLOB 字段没在索引里。
而 TEXT、BLOB、超长 VARCHAR(超过 728 字节)根本不会被任何索引存储,MySQL 会静默忽略它们是否出现在联合索引定义中。
- 执行计划里
type还是ref,但Extra从Using index变成Using where,就是最直接的信号:覆盖已失效 - 哪怕你给所有“小字段”建了联合索引,只要表里存在
TEXT,这个索引就永远无法覆盖整行 - 优化器不是“放弃索引”,而是被迫走“索引扫描 + 回表”,成本评估后可能干脆选全表扫描(顺序 I/O 比大量随机 I/O 更快)
大字段带来的额外 IO 开销
InnoDB 对大于 728 字节的字段会做行溢出处理:主记录只存 20 字节指针,真实内容存到单独的溢出页。这意味着读一行,要先读聚簇索引页,再根据指针去读溢出页——至少两次随机 I/O。
本文共计975个文字,预计阅读时间需要4分钟。
直接使用SELECT *会破坏覆盖索引,强制回表查询,这并非可能慢,而是必然慢,因为它是完全随机+I/O的过程。
为什么 SELECT * 必然让覆盖索引失效
覆盖索引生效的前提是:查询所需所有字段都完整存在于某个二级索引的叶子节点中。一旦用了 SELECT *,MySQL 就必须把整行数据捞出来——哪怕你只差一个 TEXT 或 BLOB 字段没在索引里。
而 TEXT、BLOB、超长 VARCHAR(超过 728 字节)根本不会被任何索引存储,MySQL 会静默忽略它们是否出现在联合索引定义中。
- 执行计划里
type还是ref,但Extra从Using index变成Using where,就是最直接的信号:覆盖已失效 - 哪怕你给所有“小字段”建了联合索引,只要表里存在
TEXT,这个索引就永远无法覆盖整行 - 优化器不是“放弃索引”,而是被迫走“索引扫描 + 回表”,成本评估后可能干脆选全表扫描(顺序 I/O 比大量随机 I/O 更快)
大字段带来的额外 IO 开销
InnoDB 对大于 728 字节的字段会做行溢出处理:主记录只存 20 字节指针,真实内容存到单独的溢出页。这意味着读一行,要先读聚簇索引页,再根据指针去读溢出页——至少两次随机 I/O。

