type=ALL 表示全表扫描,数据量超百万时查询延迟显著上升,主因是缺失有效索引或索引未被命中;应优先用 EXPLAIN FORMAT=TREE 分析执行路径,关注 key 是否为 NULL、rows 是否远超结果集,避免索引列函数操作,合理设计覆盖索引和游标分页。

为什么 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+ 树体积,拖慢写入和内存缓存效率。
2088shop商城购物系统是商城系统中功能最全的一个版本:非会员购物、商品无限级分类、不限商品数量、商品多级会员定价、上货库存、Word在线编辑器、订单详情销售报表、商品评论、留言簿、管理员多级别、VIP积分、会员注册积分奖励、智能新闻发布、滚动公告、投票调查、背景图片颜色更换、店标上传、版权联系方式修改、背景音乐(好歌不断)、广告图片支持Flash、弹出浮动广告、搜索引擎关健词优化、图文友情联
- 先确认高频慢查询的
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 组合模式。









