select * 在大表中性能差因需读取整行数据,造成大量无效i/o;覆盖索引可避免回表但需注意字段顺序;分区表仅在查询命中分区键时有效;索引失效常因统计信息过期,需analyze table更新。

为什么 SELECT * 在大表里特别伤性能
因为 MySQL 要从磁盘读完整行数据,即使你只用其中 2 个字段。如果表有 50 列、每行 2KB,但业务只要 id 和 status,那 95% 的 I/O 都白干了。
- 覆盖索引能避免回表:把查询涉及的字段全塞进索引里,比如
CREATE INDEX idx_user_status ON users(status, id),查SELECT id FROM users WHERE status = 'active'就不用碰聚簇索引 - 注意字段顺序:
WHERE条件字段必须在索引最左,SELECT字段放后面才可能覆盖;status在前、id在后,反过来就失效 - 别滥用:加太多字段进索引会增大 B+ 树体积,写入变慢,
TEXT或BLOB类型不能建索引
分区表不是“加了就快”,得看查询是否命中分区键
MySQL 分区(如 RANGE、LIST)本质是把一张表逻辑拆成多个子表,但查询不带分区键时,会扫所有分区——比没分区还慢,因为多了管理开销。
- 典型有效场景:按时间归档的订单表,用
order_time分区,查“最近 7 天”就能精准落到 1–2 个分区 - 无效场景:查
SELECT * FROM orders WHERE user_id = 12345,但分区键是order_time,就会全分区扫描 - 5.7+ 支持
EXPLAIN PARTITIONS:执行前加这个,看partitions列是否只显示几个分区名,不是NULL或全部名字
ALTER TABLE ... ENGINE=InnoDB 之后索引失效?其实是统计信息没更新
大表重建引擎或大批量导入后,SELECT 突然走错索引、变慢,大概率不是索引坏了,而是优化器用了过期的行数估算。
- 手动更新统计信息:
ANALYZE TABLE orders,让优化器重新采样页和索引分布 - 别依赖自动更新:大表默认采样率低(
innodb_stats_sample_pages=20),可能漏掉数据倾斜 - 验证是否生效:查
INFORMATION_SCHEMA.STATISTICS表,对比CARDINALITY值变化,或者看EXPLAIN的rows估算是否更贴近真实
联合索引字段顺序错了,WHERE a = ? AND b > ? 可能只用上一半
复合索引 (a, b) 对 a = ? AND b > ? 是有效的(用上全部字段),但对 b > ? 单独查,或者 a > ? AND b = ?,就只能用 a 部分,b 变成范围后无法继续索引下推。
- 判断依据看
EXPLAIN的key_len:比如索引是(user_id, created_at),都是 INT,总长 8 字节;查user_id = 123 AND created_at > '2023-01-01',key_len是 8;只查created_at > ...,key_len是 0 - 高基数字段放前面:比如
status只有 3 个值,user_id有百万级,索引应为(user_id, status),否则区分度太低 - ORDER BY 也要考虑:如果常查
WHERE a = ? ORDER BY b,(a, b)就比(b, a)更合适,避免额外排序
覆盖索引和分区表都容易被当成“银弹”,但真正起效的前提是查询模式和设计严格对齐。最容易被忽略的是:没验证 EXPLAIN 结果是否真用了索引,而不是只看有没有建。











