mysql集合查询与索引有什么关系_mysql性能基础解析

来源:这里教程网 时间:2026-02-28 20:41:24 作者:

集合查询(UNION/UNION ALL)本身不直接使用索引,但子查询是否走索引决定整体性能

MySQL 的

UNION
UNION ALL
是对多个
SELECT
结果集做合并操作,它本身不“走索引”,真正影响性能的是每个参与合并的子查询是否能有效利用索引。如果子查询写了
WHERE
ORDER BY
JOIN
,而对应字段没建索引,那整个
UNION
就会慢得明显——因为每个子查询都在全表扫描。

子查询中用了
WHERE user_id = 123
,但
user_id
没索引 → 全表扫两次,再合并,代价翻倍
UNION ALL
UNION
快,因为它跳过去重逻辑;但去重本身不走索引,是内存/临时表排序完成的
如果子查询结果集很大,MySQL 可能用
Using temporary; Using filesort
,这时即使子查询走了索引,合并阶段仍可能成为瓶颈

多表 UNION 场景下,索引要分别建在各子查询的过滤/连接字段上

比如你写:

SELECT id, name FROM users WHERE status = 'active'  
UNION ALL  
SELECT id, name FROM admins WHERE role = 'super'
,这两个表结构不同、数据独立,那就得各自优化:在
users(status)
admins(role)
上分别建索引。别指望一个索引跨表生效。

不要试图给
UNION
后的结果加索引——结果集是虚拟的,不能建索引
若子查询含
JOIN
,比如
SELECT u.name FROM users u JOIN orders o ON u.id = o.user_id
,则
orders(user_id)
必须有索引,否则
JOIN
退化为嵌套循环
字段类型要一致:如果
users.id
BIGINT
,而
admins.id
INT
UNION
会隐式转换,可能导致索引失效

ORDER BY + LIMIT 放在 UNION 外层时,索引大概率失效

这是高频踩坑点。例如:

(SELECT id, created_at FROM log_a WHERE type = 'error')  
UNION ALL  
(SELECT id, created_at FROM log_b WHERE type = 'error')  
ORDER BY created_at DESC LIMIT 10
,MySQL 会先把两个子查询结果全部取出、合并、排序、再截断——哪怕
created_at
有索引,也用不上外层排序。

解决方案:把
ORDER BY ... LIMIT
下推到每个子查询里(前提是语义允许),比如:
(SELECT id, created_at FROM log_a WHERE type = 'error' ORDER BY created_at DESC LIMIT 10)  
UNION ALL  
(SELECT id, created_at FROM log_b WHERE type = 'error' ORDER BY created_at DESC LIMIT 10)  
ORDER BY created_at DESC LIMIT 10
注意:下推后需在外层再
ORDER BY ... LIMIT
,否则无法保证全局 Top 10
如果子查询本身无过滤条件(如纯
SELECT * FROM t
),加索引也没用——全表扫完才排序

用 EXPLAIN 验证每个子查询是否真的用了索引

别只看最终 SQL 是否“跑出来”,要用

EXPLAIN
分别检查每个子查询。MySQL 8.0+ 支持
EXPLAIN FORMAT=TREE
,能看清执行计划树;但更简单的是把
UNION
拆开,单独对每个
SELECT
执行
EXPLAIN

关键看
type
列:要是
ALL
index
,基本没走有效索引;
ref
range
const
才算靠谱
注意
key
列是否显示实际使用的索引名;
key_len
能帮你判断是否用了联合索引的前缀
如果子查询用了
OR
LIKE '%abc'
、函数包裹字段(如
YEAR(created_at)
),索引大概率已失效——这些规则在子查询里同样适用

索引不是贴在 UNION 上的膏药,而是长在每个子查询的 WHERE、JOIN、ORDER BY 字段上的肌肉。漏掉任何一个子查询的索引,整条 UNION 就可能拖着腿跑。

相关推荐