为什么 EXPLAIN
显示 type=ALL
就该警惕
这说明 MySQL 正在全表扫描,数据量一过百万,查询延迟就明显上升。根本原因通常是缺少有效索引,或已有索引未被正确命中。比如
WHERE status = ? AND created_at > ?这类组合条件,如果只在
status上建了单列索引,MySQL 很可能弃用它而走全表扫描——因为
status的区分度低(如大量
'active'),优化器认为回表成本高于直接扫。 优先用
EXPLAIN FORMAT=TREE(MySQL 8.0+)看实际执行路径,比传统
EXPLAIN更准 关注
key字段是否为
NULL,
rows是否远超结果集数量 避免在索引列上做函数操作:
WHERE YEAR(created_at) = 2024会失效,改用
created_at >= '2024-01-01' AND created_at
联合索引的字段顺序不是随便排的
顺序决定索引能否用于范围查询和排序。核心原则是:等值查询字段在前,范围查询字段居中,排序/分组字段靠后。例如查询
SELECT * FROM orders WHERE user_id = 123 AND status IN ('paid', 'shipped') ORDER BY created_at DESC,最优索引是 (user_id, status, created_at)。
user_id = ?是等值,放最左;
status IN (...)在 MySQL 5.7+ 中可被当作等值处理,紧随其后;
created_at用于排序,放最后 反过来建
(created_at, user_id, status)就无法加速
WHERE user_id = ?,因为最左前缀不匹配 如果同时存在
WHERE user_id = ? AND created_at > ?,则
(user_id, created_at)比单列索引高效得多
覆盖索引能省掉回表,但别盲目加所有字段
当
SELECT的字段全部包含在索引中,MySQL 直接从索引树返回数据,不用再查聚簇索引(即“回表”)。这对大宽表尤其关键。但把太多字段塞进索引会显著增大 B+ 树体积,拖慢写入和内存缓存效率。 先确认高频慢查询的
SELECT列和
WHERE条件,用
SELECT列 +
WHERE等值列 + 排序列 构建最小覆盖索引 避免在索引中包含
TEXT、
BLOB或很长的
VARCHAR字段;MySQL 不支持对它们的前缀索引做覆盖 用
EXPLAIN检查
Extra是否含
Using index,这是覆盖索引生效的明确信号
大数据量下 OFFSET
分页必须重构
SELECT * FROM huge_table ORDER BY id LIMIT 100000, 20这类语句,MySQL 仍需扫描前 100020 行才能跳过,越往后越慢。这不是索引能解决的问题,得换思路。 用游标分页(cursor-based pagination):记录上一页最后的
id,下一页查
WHERE id > ? ORDER BY id LIMIT 20对非主键排序字段(如
created_at),确保该字段有索引,并用
WHERE created_at如果业务允许,把历史冷数据归档到另一张表,主表只留近 6 个月数据,索引更紧凑,查询更稳
CREATE INDEX idx_user_status_created ON orders (user_id, status, created_at);
这个索引能同时支撑「某用户的状态订单按时间倒序」查询和「某用户某状态的最新 N 条」两种场景,但如果你还常查
WHERE status = ? AND created_at BETWEEN ...,那它就帮不上忙——因为缺失最左等值列
user_id,索引就失效了。实际建索引前,一定先看慢查询日志里真实出现的
WHERE组合模式。
