在生产单神日志文件中,我们可以看到很多缓慢的查询。它们中的大多数没有使用最佳索引,因此没有使用最佳执行计划。但是,当我在mongo中自己运行相同的查询时,将使用正确的索引。So为什么对于同一个查询,我们没有相同的执行计划?
Mongodb版本: 4.0.2 (独立)
单神日志摘录:
2018-12-28T13:55:28.282+0100 I COMMAND [conn1032115] command mydb.products command: find { find: "products", filter: { origins_tags: "italia" }, sort: { last_modified_t: -1 }, skip: 320, limit: 20, singleBatch: false, maxTimeMS: 0, tailable: false, noCursorTimeout: false, awaitData: false, allowPartialResults: false, $readPreference: { mode: "secondaryPreferred" }, $db: "mydb" } planSummary: IXSCAN { last_modified_t: -1 } keysExamined:721542 docsExamined:721542 cursorExhausted:1 numYields:5637 nreturned:3 reslen:23886 locks:{ Global: { acquireCount: { r: 5638 } }, Database: { acquireCount: { r: 5638 } }, Collection: { acquireCount: { r: 5638 } } } protocol:op_query 5166ms我们可以看到以下信息:
但是,集合中存在这些索引:
db.products.createIndex({"origins_tags": 1,"sortkey": -1}, { background: true })
db.products.createIndex({"last_modified_t": -1}, { background: true })以下是优化的执行计划(预期的执行计划):
这样我们就能看到巨大的不同!
发布于 2019-01-06 00:36:41
这些索引存在于集合中: db.products.createIndex({"origins_tags":1,"sortkey":-1},{后台: true }) db.products.createIndex({"last_modified_t":-1},{背景: true })
MongoDB查询优化器根据查询形状 (查询谓词、排序和投影组合)和候选索引选择最有效的查询计划。如果一个给定查询形状有多个候选计划,则将运行一个试算,以确定哪个索引返回初始批结果(101个文档),而所测量的“工作量”最少。如果查询性能或其他情况发生变化(例如,添加或删除索引),则将缓存获胜计划和定期重新评估。
由于这两个索引都不适合您的查询,因此索引选择可能会因哪个索引在计划评估期间更快地返回初始批结果而有所不同:
origins_tags作为筛选条件,第一个索引更具有选择性,但它需要文档获取和内存中的排序(一个带有32极限的阻塞查询阶段)才能返回由last_modified排序的结果。为给定查询使用此索引所需的工作将取决于匹配文档的总数:必须为排序获取每个匹配文档。最坏的情况是需要执行内存中超过32 to的数据并导致异常的查询。origins_tags进行筛选。使用此索引所需的工作取决于找到匹配的速度:该索引可以在找到匹配时流匹配,并在有足够的匹配时立即停止。最坏的情况是对集合进行全面扫描,以确认没有更多匹配的文档。如果评估这两个计划会导致一个平分(两者似乎都执行相同的工作来返回给定查询形状的初始结果),则不需要内存中排序的计划将获胜。
您可以看到解释查询在allPlansExecution模式下进行计划评估的详细信息:db.products.find({...}).explain('allPlansExecution')。
检验员:721542人检验:721542人(基本上,整个馆藏都被检查了)
这是第二个索引的最坏情况: 721,542个文档中有323个匹配,您已经询问了结果320340(通过skip: 20,limit: 20)。
我不明白第二个指标怎么能在计划评估中给出更快的结果
计划评估只考虑哪个候选计划返回最初一批结果(101个文档),而总体努力较少。查询规划师不会在评估期间运行计划以完成计划,也不会保留任何有关键值分布的指标。缓存的查询计划将基于第二个索引不需要执行集合扫描的比较。
当mongo选择一个糟糕的计划时,它应该能够看到执行时间不太好,并且不应该为下一个查询选择相同的计划。
根据查询计划文档中的MongoDB流程图,如果性能下降,将重新评估缓存的计划。但是,如果您有多个候选计划而没有确定的胜利者,则可以选择一个查询计划,该计划对初始结果(或具有不同值的相同查询形状)更快,但对于以后的结果则不太理想。
要解决这个问题,您应该添加一个复合索引支持您的筛选和排序标准:
db.products.createIndex({"origins_tags": 1,"last_modified_t": -1}, { background: true })或者(以及不太理想的情况下)您可以使用提供索引提示强制查询使用第一个索引。请注意,如果内存中的排序需要处理超过32 to的数据,则暗示将忽略任何可能更理想的未来索引,如果内存中的排序需要处理超过32 to的数据,则也会失败。
添加建议的复合索引将导致被检查的键和文档与返回的结果数的最有效比率。
https://dba.stackexchange.com/questions/226405
复制相似问题