MongoDB未匹配复合索引前缀的查询为何选用该索引
MongoDB未匹配完整索引前缀的查询选用复合索引的场景与本次选型原因
触发该类选型的常见场景
- 查询带有明确的
limit条数限制,且所有候选索引都能匹配复合索引最左侧的等值匹配字段时,优化器会基于实际试跑的扫描成本选择索引,不会严格要求中间前缀字段完全匹配 - 跳过中间索引字段后,后续待过滤字段在复合索引上的扫描可以在极少的条目数内拿到满足limit要求的结果,整体IO、CPU成本低于其他完全匹配前缀的候选索引
- 查询可以直接通过复合索引拿到所有需要的字段(覆盖索引场景),不需要回表读取原文档,即使未匹配完整前缀,只要综合成本更低就会被选中
- 查询优化器的候选计划评估阶段,未完整匹配前缀的索引在试跑阶段产生的
works数、索引扫描条目数更少,会被直接判定为获胜计划
本次查询选中三字段复合索引的具体原因
首先需要纠正一个认知偏差:MongoDB复合索引的前缀规则从来不是“必须连续匹配所有前缀字段才能使用索引”,核心要求是必须先匹配最左侧的前缀字段,中间字段如果没有查询条件,仍然可以在左前缀字段匹配完成后,对后续字段做顺序扫描过滤,只是无法对后续字段做精准的索引定位。
本次查询的两个候选索引都满足最左前缀匹配要求(两个索引的第一个字段都是user_id,查询对user_id做了等值匹配),不存在“完全不匹配索引前缀”的情况,三字段索引只是跳过了第二个字段req.uri,直接使用第三个字段req.time_us做过滤。
最终选中三字段索引的核心原因如下:
- 查询带有
limit(20)限制,MongoDB的查询优化器不会做全量索引扫描估算,只会对每个候选索引做短时间试跑,直到拿到满足limit要求的结果数,就对比各计划的实际消耗选优。 - 从获胜计划的执行统计可以看到:使用三字段索引时,仅做了1次索引寻址(
seeks:1),总共扫描20条索引条目(keysExamined:20)就拿到了全部20条符合要求的结果,总工作单元数仅为20,没有额外无效扫描。 - 优化器试跑两字段索引
user_id_1_req.time_us_1时,拿到20条符合条件结果需要扫描的索引条目数多于20(通常是因为该user_id下,符合req.time_us时间范围的条目在三字段索引的物理存储上排布更集中,扫描时几乎不需要跳过不符合时间范围的无效条目),综合成本高于三字段索引,因此最终选择了三字段复合索引。
从执行计划的索引边界也能佐证这一点:三字段索引对req.uri设置的扫描范围是全区间[MinKey, MaxKey],但因为需要的结果量极少,全区间扫描的成本并没有高于两字段索引的精准定位成本。
内容的提问来源于stack exchange,提问作者tuabo
相关产品推荐
相关产品推荐

