索引拆分本质是依查询频率、组合及数据分布,优先为高频高选择性字段建单列索引,再按“等值在前、范围在后”设计联合索引,并利用覆盖索引减少回表,定期清理冗余索引。

理解索引拆分的本质
索引拆分不是简单地把一个大索引改成多个小索引,而是根据查询条件的使用频率、组合方式和数据分布,把高频、高选择性的过滤字段优先独立建索引,再按需补充联合索引。核心目标是让每个关键查询都能命中“最短、最匹配”的索引路径,避免全表扫描或索引失效。
先识别哪些字段值得单独建索引
重点关注 WHERE 子句中独立出现、且有较高区分度(如用户ID、订单状态、创建日期)的字段。例如:
- user_id = ? 出现频繁且基数大 → 单独建 B+ 树索引
- status IN ('paid', 'shipped') 虽然范围小,但常作为查询入口 → 可单列索引(配合覆盖优化)
- created_at > '2024-01-01' 时间范围查询多 → 单独索引 + 注意避免在它上面加函数(如 DATE(created_at))
联合索引要按“过滤强度+顺序”设计
联合索引的字段顺序不能随意。原则是:高选择性字段在前,等值查询字段在前,范围查询字段放最后。比如常见复杂查询:
-
WHERE user_id = ? AND status = ? AND created_at > ? → 推荐索引:
(user_id, status, created_at) -
WHERE status = ? AND category = ? ORDER BY updated_at DESC → 若只查少量结果,可建
(status, category, updated_at)支持过滤+排序 - 避免
(created_at, user_id)这类把范围字段放前面的组合——它无法有效支持 user_id = ? 单独查询
用好覆盖索引减少回表
如果查询只涉及少数几个字段(如 SELECT id, status, updated_at FROM orders WHERE user_id = ?),可在联合索引中直接包含 SELECT 字段:
- 建索引:
(user_id, status, updated_at, id)—— 注意 id 是主键,自动包含,但显式写出更清晰 - 这样查询完全走索引,不回主键聚簇索引,显著降低 I/O
- 但别过度覆盖:字段越多,索引体积越大,写入越慢;权衡读写比
定期验证和精简冗余索引
运行 SHOW INDEX FROM table_name 和 SELECT * FROM sys.schema_unused_indexes(MySQL 8.0+)查看未被使用的索引。特别注意:
-
(a)和(a, b)共存时,单列索引(a)往往可删(除非b基数极低或查询只查a) - 长期没命中的联合索引(尤其含三个以上字段)建议归档评估,改用更聚焦的组合
- 用 EXPLAIN FORMAT=JSON 查看 key_len、used_key_parts,确认实际用了哪些索引字段










