索引合并不是万能药:MySQL同时用两个索引,为什么比不用还慢?

大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!

某电商订单系统,开发人员给statuscreate_time分别建了单列索引。查询条件很简单:

bash 复制代码
SELECT * FROM orders 
WHERE status = 'PAID' AND create_time > '2026-09-01'
ORDER BY create_time DESC LIMIT 20;

EXPLAIN一看,possible_keys列显示两个索引,优化器选择了索引合并(Index Merge)Extra列出现Using intersect(idx_status, idx_create_time)

开发人员很高兴:"两个索引都用上了,优化器真智能!"

但实际跑起来,这条SQL在2000万数据的表上要3.8秒 。加了个FORCE INDEX(idx_create_time)强制走单个索引,反而降到了0.3秒

优化器"智能"地做了错误的决定

今天把索引合并这件事彻底拆开,讲清楚它是什么、什么时候该用、什么时候千万别用。

一、索引合并的三种类型

MySQL的索引合并优化(Index Merge Optimization)在5.0时代就引入了,官方文档把它描述为一种"优化策略",但不是"最优策略"。

1. Intersection(交集合并)

同时使用多个索引,取结果集的交集。

bash 复制代码
SELECT * FROM orders 
WHERE user_id = 12345 AND status = 'PAID';

如果user_idstatus各自有单列索引,优化器可能同时扫描两个索引,然后取交集。

2. Union(并集合并)

同时使用多个索引,取结果集的并集。

bash 复制代码
SELECT * FROM orders 
WHERE user_id = 12345 OR status = 'PAID';

这是最危险的一种------两个索引的结果集取并集,需要去重、排序,代价极高。

3. Sort-Union(排序并集合并)

先对索引扫描结果排序,再去重合并。比普通Union多了排序步骤,代价更高。

一个设计良好的复合索引通常比索引合并更高效,因为单次索引查找就能定位数据,避免了合并开销

二、索引合并的代价到底在哪?

索引合并看起来"利用了多个索引",但代价隐藏在三个地方:

代价1:多次索引扫描 + 结果集合并

索引合并需要扫描多个索引树,然后把结果集在内存中做交集或并集运算。如果每个索引扫描返回的数据量都很大,合并操作本身的开销可能超过全表扫描。

代价2:随机I/O放大

索引扫描返回的是主键值(二级索引),然后需要回表读取完整行数据。索引合并意味着多次回表------每次索引扫描都要回表一次,I/O次数成倍增加。

代价3:基数估算偏差

优化器决定是否使用索引合并,依赖于基数估算(Cardinality Estimation)。如果统计信息过期或数据分布倾斜,优化器可能错误地认为索引合并很快,但实际上慢得要命。

三、3个真实踩坑场景

坑1:OR条件导致索引合并UNION,代价远超预期

bash 复制代码
SELECT * FROM orders 
WHERE user_id = 12345 OR create_time > '2026-01-01';

优化器可能选择索引合并UNION------分别扫描idx_user_ididx_create_time,然后合并去重。

但如果user_id=12345有10万行,create_time > '2026-01-01'有50万行,合并去重要处理60万行数据------比全表扫描还慢。

坑2:索引合并的基数估算偏差

MySQL优化器基于统计信息做决策。当统计信息过期时,优化器可能低估某个索引返回的行数,从而错误地选择索引合并方案。

坑3:多个单列索引 vs 一个复合索引

很多人有个误区:给每个查询条件列都建一个单列索引,让优化器自己去"合并"。

复合索引通常比索引合并更高效------索引合并需要扫描多个索引、合并结果集、去重、排序;复合索引一次扫描就能定位到目标行。

bash 复制代码
-- 不推荐:两个单列索引让优化器去合并
CREATE INDEX idx_user_id ON orders(user_id);
CREATE INDEX idx_status ON orders(status);

-- 推荐:一个复合索引覆盖查询
CREATE INDEX idx_user_status ON orders(user_id, status);

四、什么时候该用索引合并,什么时候该用复合索引?

场景 推荐方案 原因
查询条件是AND,各条件选择性都很高 复合索引 一次索引查找定位,无合并开销
查询条件是AND,但其中一个条件选择性极低 单列索引+过滤 复合索引收益有限,索引合并代价高
查询条件是OR,各条件选择性都很高 索引合并UNION可接受 无法用单个复合索引覆盖OR条件
查询条件是OR,但结果集很大 改写SQL或用UNION ALL 避免索引合并的去重和排序开销
查询条件经常变化,无法预建复合索引 索引合并作为兜底 聊胜于无,但需监控性能

五、怎么判断优化器是否选错了?

方法一:对比执行计划

分别用FORCE INDEX强制走单个索引和让优化器自由选择,对比响应时间。

方法二:查看EXPLAINExtra

  • Using intersect(...) → 交集合并,通常AND条件触发

  • Using union(...) → 并集合并,通常OR条件触发

  • Using sort_union(...) → 排序并集合并,代价最高

方法三:用EXPLAIN ANALYZE看实际行数

EXPLAIN ANALYZE会输出每个步骤的实际执行行数。如果actual rows远大于优化器估算的rows,说明基数估算有偏差。

方法四:使用OPTIMIZER_TRACE

开启optimizer_trace,可以看到优化器在索引合并和其他方案之间的代价对比,精确了解优化器为什么选了索引合并。

六、小结

索引合并是优化器的"兜底方案",不是"首选方案"。能用复合索引解决的,优先用复合索引。如果EXPLAIN里出现了Using intersectUsing union,先确认索引合并的真实代价------很多时候,一个设计良好的复合索引比索引合并快一个数量级。索引合并的出现往往暗示你的索引设计还有优化空间。

小耶在手,SQL 不愁

还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽......我们下次见~

相关推荐
YOLO数据集集合10 小时前
红外热成像人员检测数据集 |红外检测 白热模式 人员检测 夜间监控 目标检测 YOLO格式 深度学习数据集 计算机视觉9067期
数据库·深度学习·yolo·目标检测·计算机视觉·红外人员·红外数据
BUG指挥官10 小时前
Redis已接入AI
数据库·人工智能·spring boot·redis·spring cloud
SkyWalking中文站19 小时前
BanyanDB 0.11.0:新特性与升级指南
数据库
₍˄·͈༝·͈˄*₎◞ ̑̑码20 小时前
MyBatis操作数据库
数据库·mybatis
刃神太酷啦1 天前
Redis 进阶核心:持久化 (RDB/AOF)、事务与主从复制全解析----《Hello Redis!》(5)
linux·c语言·数据库·c++·redis·缓存·bootstrap
丁丁点灯o1 天前
Oracle中使用外键的场景及不适用外键的情况
数据库·oracle
天天进步20151 天前
Pixelle-Video 源码解析 #18:声音克隆功能:参考音频如何影响解说效果?
数据库·音视频
不懂的浪漫1 天前
ToDesk 连接 Linux 后分辨率过低的解决方法
linux·运维·数据库
布莱克6051 天前
Redis 详解:从核心数据结构到高可用架构
数据库
冰暮流星1 天前
mysql之左外连接与右外连接
数据库·sql