You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

MongoDB索引设计基础技能测试第4题疑问求助

MongoDB索引设计基础技能测试第4题疑问求助

我刚完成了MongoDB的索引设计基础技能测试,但有一道题的答案我实在搞不懂,想请教大家!

题目内容:

  1. 一名开发人员正在为任务列表应用优化MongoDB集合的性能。该集合存储的任务包含userIddueDateprioritystatus等字段。开发人员确定了两个关键查询:
db.tasks.find({userId: "3f9ab41"}).sort({dueDate: -1}); // Query 1
db.tasks.find({userId: "3f9ab41", priority: "high"}); // Query 2

开发人员应该使用什么索引来高效支持这些查询?

  • { priority: 1, userId: 1, dueDate: -1 }
  • { userId: 1, dueDate: -1 }
  • { userId: 1, priority: 1 }
  • { userId: 1, dueDate: -1, priority: 1 }

选择1个选项。

我当时选了第二个选项{ userId: 1, dueDate: -1 },我觉得索引的前缀顺序里userId能被第二个查询用到,而且这个索引完全覆盖第一个查询的需求。但结果是错的,正确答案是第四个选项{ userId: 1, dueDate: -1, priority: 1}。我重新单独做这道题试了所有选项,确认了正确答案是第四个,可还是没搞懂原因,有没有大佬能帮我解释一下?


问题解析

咱们先分别拆解两个查询的核心需求,再看索引的匹配逻辑:

先看Query 1:db.tasks.find({userId: "3f9ab41"}).sort({dueDate: -1})

这个查询的逻辑是:先精准匹配userId,然后对匹配到的结果按dueDate降序排序

  • 第二个选项{ userId: 1, dueDate: -1 }确实能完美支持这个查询:前缀userId:1用来快速筛选出目标用户的所有任务,后面的dueDate:-1刚好和排序方向一致,MongoDB可以直接利用索引的有序性返回结果,不需要额外做排序操作。

再看Query 2:db.tasks.find({userId: "3f9ab41", priority: "high"})

这个查询是同时精准匹配userIdpriority

  • 如果你用第二个选项的索引{ userId: 1, dueDate: -1 },MongoDB确实能通过userId前缀筛选出目标用户的任务,但接下来要找priority:"high"的文档时,它只能在筛选出的所有用户任务里逐个扫描(也就是所谓的“索引扫描后过滤”),因为这个索引里根本不包含priority字段的信息。这时候如果用户的任务很多,这个过滤过程就会很慢,效率不高。
  • 而第四个选项的索引{ userId: 1, dueDate: -1, priority: 1 }呢?它的前缀userId:1同样能快速筛选目标用户,而且因为priority字段被包含在索引里,MongoDB可以直接在索引里完成priority:"high"的匹配,不需要回表扫描原文档,也不需要额外过滤,效率更高。

为什么这个索引能同时满足两个查询?

MongoDB的索引是前缀匹配的,而且如果索引包含了查询需要的所有筛选/排序字段,就可以实现“覆盖索引”或者高效的查询支持:

  1. 对于Query 1:用索引的前两个字段userId:1, dueDate:-1,完全满足筛选+排序的需求,不需要用到第三个字段priority,前缀匹配生效。
  2. 对于Query 2:用索引的第一个字段userId:1筛选用户,第三个字段priority:1用来精准匹配,虽然中间夹了dueDate:-1,但MongoDB可以跳过这个字段直接利用索引里的priority信息(因为索引里已经包含了这个字段,相当于在筛选出的用户索引条目里直接找priority符合条件的,不需要回表)。

再对比其他选项

  • 第一个选项{ priority: 1, userId: 1, dueDate: -1 }:前缀是priority,两个查询都是先按userId筛选,这个索引的前缀不匹配,完全不适合。
  • 第三个选项{ userId: 1, priority: 1 }:能支持Query 2,但Query 1需要按dueDate排序时,只能先筛选出用户任务,再在内存里排序(如果结果集大的话会触发磁盘排序,很慢),因为索引里没有dueDate的排序信息。

所以第四个选项是唯一能同时高效支持两个查询的索引:既让Query1不用额外排序,又让Query2不用额外过滤,完美覆盖两个场景的性能需求。


备注:内容来源于stack exchange,提问作者Travis

火山引擎 最新活动