DDS查询计划及查询重规划
DDS查询计划是决定查询如何执行的详细过程。DDS查询优化器会根据查询的条件、数据分布、索引信息等选择一个执行计划,以确保查询的执行效率。然而,在某些情况下,DDS会重新评估并生成新的查询计划,即查询重规划。用户理解何时发生查询重规划以及如何应对这一过程,这将有助于提升数据库性能并有效减少资源浪费。
以下将介绍DDS查询计划的工作原理,查询重规划的触发场景,以及处理和优化查询重规划的最佳实践。
查询计划的概述
查询计划是在执行查询时,DDS查询优化器选择的执行路径。这个执行路径定义了如何扫描数据,是否使用索引,以及如何处理查询的每个阶段。DDS通过以下几个步骤生成查询计划:
- 解析查询:DDS解析查询条件,识别字段和操作符。
- 选择索引:基于查询的条件,DDS会检查可用的索引,并选择最适合的索引来加速查询。
- 评估执行路径:DDS会基于数据量、字段选择性、索引类型等评估可能的执行路径,最终选择最优的执行路径生成新的查询计划。
- 执行查询:DDS按照选定的查询计划执行查询并返回结果。
查询重规划的概述
查询重规划(Query Re-Planning)是指DDS在执行查询过程中,基于数据变化或查询结构的变化,重新评估并生成一个新的查询计划。查询重规划发生时,DDS会放弃之前选择的查询计划,采用新的执行路径。
查询重规划的主要目的是在数据环境变化时,确保查询能够适应新的数据分布、索引变化和负载变化,从而维持查询性能的稳定。
查询重规划的触发场景
以下是一些常见的查询重规划场景:
- 索引变化
新增或删除索引:当某个字段的索引发生变化(如新增索引或删除索引)时,DDS可能会重新规划查询执行计划。查询可能会重新评估现有索引的有效性,进而选择新的索引,或决定使用全表扫描。
- 数据分布变化
如果数据分布发生了变化(例如某个字段的选择性发生显著变化),DDS可能会重新评估查询计划,以确保选择最佳的执行路径。例如,某个字段变得更加稀疏或密集,导致索引的效益变化。
- 查询条件变化
当查询条件变化时,DDS可能需要重新生成查询计划。例如,某些查询添加了新的筛选条件,或者查询条件中使用了不同的操作符(如从$eq改为$in)。
- 聚合管道中的变化
在聚合操作中,如果数据量、字段或管道顺序发生变化,DDS可能会重新评估聚合管道的执行计划。尤其是当管道阶段的数据过滤条件发生变化时,DDS可能会重新选择执行路径。
- 环境资源压力变化
在实例负载发生变化(如节点资源耗尽、硬盘空间不足、或网络延迟增加)时,DDS可能会重新调整查询计划,以便在当前资源条件下优化查询性能。
- 查询缓存失效
DDS会将之前执行的最优的查询计划缓存,DDS在某些情况下可能会使用查询缓存。随着查询缓存的失效,查询计划可能会被重新生成。
查询重规划的影响
发生重规划时,会在慢日志中看到关键字:replanned:1,这表示数据库无法针对当前查询模式的查询条件提供始终有效的计划。
频繁的查询重新规划(Query Re-Planning)可能带来以下影响:
- CPU开销增加:频繁的重新规划会消耗更多CPU资源,影响整体性能。
- 内存消耗增加:频繁重新规划会增加内存使用,可能影响其他操作。
- 查询延迟增加:每次重新规划都会增加查询执行时间,导致响应变慢。
查询重规划的处理建议
- 定期监控查询计划
建议定期监控查询的执行计划,特别是在系统发生变动后,如添加索引、新增数据或调整查询结构。通过以下方式实现:
- 使用explain()方法分析查询的执行计划。
- 定期检查慢日志,了解可能的查询计划变化。
- 观察查询计划变化的原因
当查询重规划发生时,首先要确定是哪些因素导致了查询计划的变化。根据以下几个方面进行排查:
- 索引变化:使用db.collection.getIndexes()检查现有索引,确认是否有索引的新增或删除。
- 数据分布:通过统计信息来检查数据是否发生了显著变化。例如使用db.collection.stats()查看集合的统计信息,如文档数、数据大小和索引大小。
- 查询条件:检查查询条件是否发生了变化,是否增加了新的字段或者修改了查询操作符。
- 针对查询重规划进行优化
- 【推荐】确保数据库环境有足够的资源(CPU、内存、磁盘等)来处理负载,减少因资源限制导致的查询重规划,升级实例规格以缓解数据库负载压力,具体操作请参考文档变更实例的CPU和内存规格。
- 【推荐】预估数据分布:在设计索引时,可以使用数据分布的预测来选择合适的字段进行索引,以避免数据分布变化时引发频繁的查询重规划。
- 【推荐】使用稳定的索引:确保为常用查询提供稳定且合适的索引。特别是在数据量较大时,索引可以有效减少查询重规划的发生。
- 【推荐】尽量避免频繁变化的查询结构:查询条件的频繁变动可能会导致DDS经常重新生成查询计划。建议根据业务需求,尽量确保查询条件的一致性。
- 【推荐】聚合优化:如果您的查询涉及复杂的聚合管道,建议定期分析管道的执行计划并进行优化。可以通过$match在聚合管道的早期阶段过滤数据,减少后续阶段的数据处理量。
- 对发生replan的查询条件使用hint()来指定索引,对查询调用此方法可覆盖DDS的默认索引选择和查询优化过程。示例:
db.users.find({gender:"M"},{user_name:1,_id:0}).hint({gender:1,user_name:1})
- 在发生replan的查询条件使用索引过滤器来限制使用的索引,索引过滤器会覆盖查询计划器正常选择查询计划的行为,当一个查询同时存在Hint和索引过滤器时,索引过滤器会覆盖指定的Hint值,因此,您应当谨慎使用索引过滤器。以下示例为orders集合创建了一个索引筛选器。该筛选器适用于其谓词为item字段的等值匹配的查询,其中仅投影quantity字段,并指定按order_date升序排序。
db.runCommand( { planCacheSetFilter: "orders", query: { item: "ABC" }, projection: { quantity: 1, _id: 0 }, sort: { order_date: 1 }, indexes: [ { item: 1, order_date: 1 , quantity: 1 } ] } )
对于计划缓存查询结构,查询优化器将仅考虑使用索引{ item: 1, order_date: 1, quantity: 1 }的索引计划。
评估重规划并采取措施
查询重规划可能会导致执行计划的变化,进而影响查询的性能。在重规划发生时,需要评估查询性能的变化,检查是否有提升或下降,并相应地采取措施:
- 性能下降:如果发现查询性能因查询重规划而下降,可以通过添加或调整索引来改善性能。
- 性能提升:如果重规划后的查询执行更高效,可以继续保持当前的执行计划,监控是否有其他潜在的性能瓶颈。