跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

SQL 单列索引与复合索引最佳实践

单列索引足够。WHERE仅用一个字段时,建INDEX(a)即可高效命中,无需提前建复合索引;复合索引虽能通过最左前缀匹配生效,但浪费空间和写入性能,且对WHERE b = ?完全无效。 WHERE 条件里只用一个字段,该建单列索引还是复合索引? 单列索引足够。MySQL、PostgreSQL 等主流数据库对
WHERE a = ?
这类查询能高效命中
INDEX(a)
,不需要为了“以后可能加条件”提前建
INDEX(a, b)
。复合索引在单字段查询时虽可能被用上(最左前缀匹配),但会浪费磁盘空间和写入开销,更新
b
字段时也要维护冗余索引项。 单列索引体积小,B+ 树层级更低,点查更快 复合索引
INDEX(a, b)
对
WHERE a = ?
有效,但对
WHERE b = ?
完全无效 如果业务明确只按
a
过滤,建
INDEX(a)
更干净;后续真要查
a AND b
,再补
INDEX(a, b)
也不迟 WHERE 同时过滤两个字段,为什么
INDEX(a, b)
比
INDEX(a)
+
INDEX(b)
快? 因为 MySQL 通常一次查询只用一个索引(5.7+ 的 index merge 是例外,但代价高、不可靠)。
WHERE a = ? AND b = ?
用两个单列索引,优化器大概率退化为全表扫描或只选其一,再回表过滤另一个条件;而
INDEX(a, b)
能直接定位到唯一叶子节点,避免回表或二次过滤。 复合索引的 B+ 树按
a
排序,
a
相同时再按
b
排序,天然支持等值+等值联合查找
INDEX(a)
+
INDEX(b)
在
AND
场景下几乎不协同工作,除非走 index merge(查看执行计划里是否出现
index_merge
) 注意顺序:
INDEX(a, b)
对
WHERE a = ? AND b = ?
高效,但对
WHERE b = ?
无效 ORDER BY 和 WHERE 混用时,复合索引字段顺序怎么排? 优先满足
WHERE
的等值条件,再接
ORDER BY
字段。例如
WHERE status = 'active' ORDER BY created_at DESC
,应建
INDEX(status, created_at)
。这样索引能先快速定位所有
status = 'active'
的行,且这些行在索引中已按
created_at
排好序,无需额外排序。 等值条件(
=
、
IN
)可作为索引前导列;范围条件(
>
、
BETWEEN
)之后的字段无法用于索引排序
WHERE a > 10 ORDER BY b
用
INDEX(a, b)
时,
b
部分不参与排序(因为
a
是范围扫描,
b
在各
a
分组内有序,但跨组无序) 如果
ORDER BY b DESC, c ASC
,索引字段顺序必须严格对应,且方向需一致(MySQL 8.0+ 支持混合方向,但老版本只认全部 ASC 或全部 DESC) 建了复合索引,EXPLAIN 却显示没用上,常见原因有哪些? 最常见的是条件没触达最左前缀,或隐式类型转换导致索引失效。
WHERE b = ?
不会用
INDEX(a, b)
,哪怕
b
是第二列 字段类型和参数类型不一致:比如
user_id
是
BIGINT
,但 SQL 里写成
WHERE user_id = '123'
(字符串),触发隐式转换,索引失效 对索引字段用了函数:
WHERE YEAR(created_at) = 2024
无法用
INDEX(created_at)
,应改写为
WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01'
使用了
!=
、
NOT IN
、
LIKE '%xxx'
等无法走索引的表达式 索引不是建了就万事大吉,每条查询都得看
EXPLAIN
输出里的
key
和
rows
字段——前者告诉你用了哪个索引,后者暴露实际扫描行数,比“是否用索引”更能说明问题。

相关文章