复合索引设计:高频查询路径全覆盖的 SQL 优化

CLARA轻量论坛系统
CLARA轻量论坛系统 星耀SVIP管理员 黑卡会员
发布于 2026-08-20 17:38 ·21 浏览 ·0 回复

结论:复合索引设计的目标不是「每个查询各建一个索引」,而是把高频查询路径按「等值列 → 范围/排序列 → 覆盖列」的顺序收敛到少数几个索引上,做到高频 SQL 全部走索引、尽量不回表,同时不让写放大失控。

高频查询路径先列清单,再谈索引

结论:索引设计的起点是一份高频 SQL 清单,不是表结构本身。

先开慢查询日志或看监控,把 QPS 最高、最慢的 5-10 条 SQL 抄出来,标注每一列的角色:等值条件、范围条件、排序、分组、返回列。比如一张订单表 orders(id, user_id, status, amount, created_at),高频路径通常是这三条:

-- Q1 我的订单列表
SELECT id, amount, created_at FROM orders
WHERE user_id = ? AND status = ? ORDER BY created_at DESC LIMIT 20;

-- Q2 后台按状态查当天新增
SELECT COUNT(*) FROM orders WHERE status = 1 AND created_at >= ?;

-- Q3 单笔详情
SELECT * FROM orders WHERE id = ?;

只有看清楚「谁是等值、谁是范围、谁在排序」,后面的列顺序才有依据。

三条排序规则:等值最左、范围最后、排序列跟上

结论:复合索引列顺序的通用规则是——等值条件列放最左,范围条件列放最后,排序列紧跟范围列,且排序方向保持一致。

第一条是最左前缀原则:索引 (a, b, c) 能支撑 a、a+b、a+b+c 三种查询,但跳过 a 直接查 b 用不上。第二条是范围列会截断后续列的定位能力,`WHERE status = 1 AND created_at >= ?` 里 created_at 是范围,就别再往它后面放用于筛选的等值列。第三条是排序,ORDER BY 的列必须和索引顺序一致、方向统一,才能免掉 filesort。

注意点:MySQL 5.7 不支持真正的降序索引,写 `KEY idx(a, b DESC)` 里的 DESC 会被解析后忽略;需要正反混合排序时通常要升级到 8.0。

能用覆盖索引就用,这是性价比最高的一步

结论:把 SELECT 里用到的列补到索引尾部做成覆盖索引,能直接省掉回表,通常是收益最大的一次优化。

给 Q1 建 `KEY idx_user_status_time (user_id, status, created_at, amount)`:前两列等值定位,created_at 负责排序,amount 只用于返回。尽管 amount 在 created_at 后面,但因为前缀是等值,created_at 的整体有序性不受影响,`ORDER BY created_at DESC LIMIT 20` 依然可以顺扫索引直接取 20 行,Extra 里能看到 `Using index`。

代价要说清楚:索引越长,写入和存储成本越高。覆盖索引只给「高频 + 返回列少」的查询用,`SELECT *` 的查询基本没法覆盖,别硬凑。

用 EXPLAIN 验证,不要凭感觉

结论:索引建完必须 EXPLAIN 验证,重点看 type、key、rows、Extra 四个字段。

目标是 type 达到 ref/range(主键或唯一键等值可到 const),key 与你设计的索引一致,rows 数量级合理,Extra 里没有 `Using filesort` 和 `Using temporary`,出现 `Using index` 说明命中覆盖。同时算一下选择性:区分度 = 不同值数量 / 总行数,接近 1 的列(如 user_id)适合放前面,接近 0 的列(如性别、状态只有两三个值)单独建索引几乎没用,只能作为复合索引的后续列。

砍冗余、控数量,索引不是越多越好

结论:单表索引建议控制在 5-6 个以内,(a, b) 已经覆盖了只查 a 的场景,单独的 a 索引属于冗余。

每加一个索引,INSERT/UPDATE/DELETE 都要多维护一棵 B+ 树。落地时按「一条高频路径对应一个索引」收敛,多个查询如果能共用同一个最左前缀就合并。MySQL 5.7 加索引可以用 `ALTER TABLE orders ADD KEY idx_x (...), ALGORITHM=INPLACE, LOCK=NONE` 做在线变更,避免长时间锁表。

顺手提一句:如果你是在给 Clara BBS 写插件、需要给自建表加字段或加索引,不必手动上服务器改表,覆盖上传新文件后进后台「系统工具→数据库升级」执行一次即可,它跑的是幂等增量 DDL,可重复执行,新增列与表会自动补齐。

收尾

复合索引设计就四步:列高频查询、按「等值→范围/排序→覆盖」定列序、用 EXPLAIN 验证、砍掉冗余控制数量。判断标准很朴素——高频 SQL 的 type 是 ref/range、Extra 无 filesort、返回列被索引覆盖,就算达标;剩下的都是写放大和选择性的取舍问题。

本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-500.html
转载请注明出处,版权归原作者所有。

全部回复 0

还没有回复,来抢沙发~