MongoDB系列之查询计划

概述

一个查询具体如何被执行的过程,称为查询计划。MongoDB采用自底向上的方式来构造查询计划,每一个查询计划(query plan)都会被分解为若干个有层次的阶段(stage)。整个查询计划最终会呈现出一颗多叉树。
在这里插入图片描述
整个计算过程是从下向上投递的,每一个阶段的计算结果都是其上层阶段的输入,每一个阶段都有自己的逻辑语义。

执行模式

explain有3种执行模式:

  • queryPlanner:默认,仅进行查询计划分析,输出计划中的阶段信息。
  • executionStats:执行模式,在查询计划分析后,将按照winningPlan执行查询并统计过程信息。
  • allPlansExecution:全计划执行模式,将执行所有计划(包括winningPlan和rejectPlans),并返回全部的过程统计信息。

根据MongoDB版本不同,explain命令的输出结果包含不同的丰富的信息(字段)。以MongoDB 6.0.5版本为例,执行命令db.game.find().explain();db.game.find().explain('queryPlanner');,输出完全一样:

[
  {
    "command": {
      "find": "game",
      "filter": {
      },
      "$db": "test"
    },
    "explainVersion": "1",
    "ok": 1,
    "queryPlanner": {
      "namespace": "test.game",
      "indexFilterSet": false,
      "parsedQuery": {
      },
      "queryHash": "17830885",
      "planCacheKey": "17830885",
      "maxIndexedOrSolutionsReached": false,
      "maxIndexedAndSolutionsReached": false,
      "maxScansToExplodeReached": false,
      "winningPlan": {
        "stage": "COLLSCAN",
        "direction": "forward"
      },
      "rejectedPlans": []
    },
    "serverInfo": {
      "host": "johnnydeMBP.ada.local",
      "port": 27017,
      "version": "6.0.5",
      "gitVersion": "c9a99c120371d4d4c52cbb15dac34a36ce8d3b1d"
    },
    "serverParameters": {
      "internalQueryFacetBufferSizeBytes": 104857600,
      "internalQueryFacetMaxOutputDocSizeBytes": 104857600,
      "internalLookupStageIntermediateDocumentMaxSizeBytes": 104857600,
      "internalDocumentSourceGroupMaxMemoryBytes": 104857600,
      "internalQueryMaxBlockingSortMemoryUsageBytes": 104857600,
      "internalQueryProhibitBlockingMergeOnMongoS": 0,
      "internalQueryMaxAddToSetBytes": 104857600,
      "internalDocumentSourceSetWindowFieldsMaxMemoryBytes": 104857600
    }
  }
]

部分字段解读:

  • queryPlanner:描述查询计划
  • queryPlanner.indexFilterSet:是否设置indexFilter,indexFilter可决定查询优化器对于某个查询将如何使用索引
  • serverInfo:MongoDB服务器概要信息
  • serverParameters:参数设置

执行命令db.game.find().explain('executionStats');,输出内容除executionStats字段外,和db.game.find().explain('queryPlanner');完全一致,都出部分:

"executionStats": {
  "executionSuccess": true,
  "nReturned": 0,
  "executionTimeMillis": 0,
  "totalKeysExamined": 0,
  "totalDocsExamined": 0,
  "executionStages": {
    "stage": "COLLSCAN",
    "nReturned": 0,
    "executionTimeMillisEstimate": 0,
    "works": 2,
    "advanced": 0,
    "needTime": 1,
    "needYield": 0,
    "saveState": 0,
    "restoreState": 0,
    "isEOF": 1,
    "direction": "forward",
    "docsExamined": 0
  }
}

executionStats:执行过程统计,捕获计划在执行过程中的相关信息,只有在executionStats或allPlansExecution模式下才会输出。

执行命令db.game.find().explain('allPlansExecution');相比于db.game.find().explain('executionStats');,输出结果多一个allPlansExecution字段。

IndexFilter

queryPlanner.winningPlan.stage

queryPlanner.winningPlan.stage参数如下:

类型描述
COLLSCAN全表扫描
IXSCAN索引扫描
FETCH根据索引检索指定的文档
SHARD_MERGE将各个分片的返回结果进行merge
SORT在内存中进行排序
LIMIT使用limit限制返回结果数量
SKIP使用skip进行跳过
IDHACK针对_id字段进行查询
SHANRDING_FILTER通过mongos对分片数据进行查询
COUNT利用db.coll.explain().count()进行count运算
COUNTSCAN不使用index进行count时
COUNT_SCAN使用Index进行count时
SUBPLA未使用到索引的$or查询的stage返回
TEXT使用全文索引进行查询时候的stage返回
PROJECTION限定返回字段时候stage的返回

缓存

一个查询操作可能对应多个不同的查询计划。无论是哪一种方式,都会先经过内部的评分机制进行评估,最终选出一个最优的执行方案。查询计划的评估必然会产生一定的计算开销。

MongoDB提供PlanCache用以实现查询计划缓存能力,可避免在一定条件内对同一个查询模型进行重复性的分析和评估工作。如果对于某个查询已经有了确定性的选择,查询优化器会直接做出选择,此时缓存并不会启用。
在这里插入图片描述
步骤解读:

  • 查询开始执行,判断PlanCache中是否有对应的缓存。
  • 如果没有缓存,则进入计划生成阶段,执行如下步骤:
    • 分析查询语句与全部索引,产生候选计划。
    • 评估查询计划,包括对计划的并发执行、采样。
    • 选择最优的计划。
    • 将计划存入缓存。
  • 如果存在缓存,则触发replanning机制,评估查询性能,此时有两种结果:
    • 查询性能不达标,淘汰缓存重新生成计划。
    • 查询性能达标,采纳计划。
  • 执行最终计划,返回结果。

查询模型(query shape)是对于当前查询场景的唯一性结构描述。查询优化器会首先将查询请求解析为某个查询模型,根据这个模型再进行计划缓存的查询。查询模型的组成包括以下3个部分:

  • query:描述查询条件的结构,该结构由条件的字段、操作符(谓词)以及条件的嵌套关系组成,并不包含查询条件的具体值
  • projection:描述即将返回哪些字段
  • sort:描述排序的规则。

如果同时存在多个候选计划,那么需要根据一种评分机制从这些计划中选出一个最优计划,这就涉及计划的评优(evaluate)过程。

首先,让所有计划都同时执行一定量的扫描任务,扫描任务在满足以下条件时停止:

  • 扫描次数达到numWorks次,numWorks=Math.max(10000, 0.3×collection.count)
  • 返回结果达到numResults个,numResults=Math.min(101, query.getN(), query.getLimit()),其中query.getN()来自getMore命令,query.getLimit()则只有限制limit条件才会出现。这两个参数只有存在时才会参与比较,否则numResults默认就是101。

为每个计划的执行情况打分,计算分数的因子来自下面几点:

  • isEOF是否为true,如果出现isEOE则说明扫描的指针已经到达末尾。如果计划提前结束,则扫描会获得最大的机会。
  • advance/workUnits(%),如果返回的结果数占扫描数的比例越大,则代表扫描效率越高。

是否存在以下低效率的阶段:

  • PROJECTION+FETCH(非覆盖索引查询)
  • SORT(内存排序)
  • AND_HASH|STAGE_AND_SORTED(索引正交阶段)。

任意一种低效阶段的存在都会导致候选计划被扣分。

最后,根据所得分数进行排序,得分最高的计划被评选为最优计划并写入缓存。

对于已经缓存的计划,MongoDB仍会采用一种replaining的机制来保证其高效性。在返回缓存的计划之前,首先对该计划进行扫描采样,这次的采样数相比之前的numWorks会扩大10倍:

  • 扫描过程中如果返回numResults个条目,或者到达EOF,则达到通过(pass)条件,此时仍然选用此计划。
  • 如果超过采样数之后仍未达到通过条件,则转为失败(fail)状态,触发replain过程,此时将重新评选计划。
  • 如果扫描过程中出错,则同样会变成失败状态,此时也会触发replain过程。

触发查询计划缓存清除的时机:

  • 执行PlanCache.clear方法。
  • 创建、删除索引或者执行集合的drop操作。
  • 重启MongoDB进程。

强制命中

在某些极少情况下,某些查询产生的最终计划可能并不是你想要的。事实上,想实现一个完美的查询计划是非常困难的,MongoDB在查询优化器上做了很多合理性方面的努力,如提供一些干预的手段。

hint方法实现对查询计划机制的干预,在查询计划中使用hint语句可以让MongoDB忽略查询优化器的结果,从而直接使用指定的索引。hint方法参数可以传入索引的定义对象,也可以是索引的名称。

IndexFilter方法也可以于预查询索引:

db.runCommand(
  {
    planCacheSetFilter: "test",
    query: {a: 3, b: 4},
    index: [{b: 1}]
  }
)

执行planCacheSetFilter命令会在test集合中增加一个IndexFilter对象,该对象将会自动关联到同时包含a字段与b字段的等值查询,并引导查询优化器使用{b: 1}这个索引。

IndexFilter的优先级高于hint方法,如果查询优化器发现关联的IndexFilter,则一定会忽略hint语句。但是,IndexFilter并不能保证查询优化器最终一定会选择对应的索引,事实上优化器会将这些索引与全表扫描方式一并进行评估,再抉择出最终的结果。在最坏的情况下,如果指定不存在的索引,就会导致全表扫描。

IndexFilter是内存态的,重启MongoDB则会自动失效。另外,也可以使用planCacheClearFilters进行擦除。

参考

  • MongoDB进阶与实战:微服务整合、性能优化、架构管理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/474054.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

3个Tips,用“AI”开启新生活

相信最近,很多朋友们都回归到了忙碌的生活节奏中。生活模式的切换,或多或少会带来身体或情绪状况的起伏。新技术正在为人们生活的方方面面带来便利。3个小Tips或许能让你也从新技术中获益,从身到心,用“AI”开启新生活。 关”A…

【机器学习】基于正余弦搜索算法优化的BP神经网络分类预测(SCA-BP)

目录 1.原理与思路2.设计与实现3.结果预测4.代码获取 1.原理与思路 【智能算法应用】智能算法优化BP神经网络思路【智能算法】正余弦优化算法(SCA)原理及实现 2.设计与实现 数据集: 多输入多输出:样本特征24,标签类…

基于深度学习的面部情绪识别算法仿真与分析

声明:以下内容均属于本人本科论文内容,禁止盗用,否则将追究相关责任 基于深度学习的面部情绪识别算法仿真与分析 摘要结果分析1、本次设计通过网络爬虫技术获取了七种面部情绪图片:吃惊、恐惧、厌恶、高兴、伤心、愤怒、自然各若…

Python Qt Designer 初探

代码下载在最下面 #开发环境安装# 本示例在Windows11下, 使用VSCode开发, Python 3.12.2, Qt Designer 5.11 VSCode插件Python、Python Debugger、PYQT Integration、Pylance (准备) VSCode自行官网下载 Visual Studio Code - Code Editing. Redefined (准备) Python 直接…

腾讯和香港中文大学发布文字生成视频AI模型DynamiCrafter

前言 在数字化时代,视觉内容的创造和动态化已成为创意表达和信息传递的重要工具。最近由香港中文大学、腾讯AI Lab联合研发的视频AI模型DynamiCrafter,这一模型能够将静态图像转化为逼真的动态视频,开创了文本到视频生成技术的新纪元。 Hugg…

matlab 将矩阵写入文件

目录 一、概述1、算法概述2、主要函数二、将矩阵写入到文本文件三、将矩阵写入电子表格文件四、将矩阵写入指定的工作表和范围五、将数据追加到电子表格六、将矩阵数据追加到文本文件七、参考链接本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此…

Go微服务实战——服务的监控与链路追踪(监控数据可视化)

链路追踪背景 对于早期系统或者服务来说,开发人员一般通过打日志的方式来进行埋点(常用的数据采集方式),然后再根据日志系统和性能监控定位及分析问题。对于单体的应用通过日志系统完全可以定位到问题,从而排查异常。…

MySQL B+树索引 和 Redis 中跳表索引的区别

一、MySQL B树索引 和 Redis 中跳表索引 在 MySQL 中常用的索引是 B树索引,而 Redis 中,例如 zset 使用的的是跳表索引,两者有什么区别呢,MySQL 为什么不使用 跳表 呢?或者说 Redis 中为什么不使用 B树 呢&#xff1f…

【Flutter 面试题】Flutter如何进行本地存储和缓存数据?

【Flutter 面试题】Flutter如何进行本地存储和缓存数据? 文章目录 写在前面口述回答补充说明实际案例完整代码示例运行结果详细说明 写在前面 🙋 关于我 ,小雨青年 👉 CSDN博客专家,GitChat专栏作者,阿里云…

分布式之Skywalking

Skywalking skywalking是一个apm系统,包含监控,追踪,并拥有故障诊断能力的 分布式系统 一、Skywalking介绍 1.什么是SkyWalking Skywalking是由国内开源爱好者吴晟开源并提交到Apache孵化器的产品,它同时吸收了Zipkin /Pinpoint …

力扣Lc19--- 268. 丢失的数字(java版)-2024年3月20日

1.题目描述 2.知识点 (1)比如数组里面有n个数,然后计算这n个数的总和(用等差求和数列计算),然后减去数组的和,用总和减去数组和即为所得 (2)加强型 for 循环(也称为 for-each 循环&…

[C语言]指针笔试题

题一、 //结构体的大小是20个字节 struct Test{int Num;char *pcName;short sDate;char cha[2];short sBa[4];}*p;//假设p 的值为0x100000。 如下表表达式的值分别为多少? //已知,结构体Test类型的变量大小是20个字节 int main(){printf("%p\n"…

【transformer模型】一篇文章讲透

目录 引言 一、引言 二、Transformer模型的基本结构 1 编码器(python代码片段) 2 解码器 三、自注意力机制的工作原理 四、Transformer模型的应用场景 1 机器翻译 2 文本摘要 3 情感分析 4 语音识别 五、Transformer模型的发展现状及未来趋势…

【C语言】结构体内存对齐问题

1.结构体内存对齐 我们已经基本掌握了结构体的使用了。那我们现在必须得知道结构体在内存中是如何存储的?内存是如何分配的?所以我们得知道如何计算结构体的大小?这就引出了我们今天所要探讨的内容:结构体内存对齐。 1.1 对齐规…

Python利用pygame实现飞机大战游戏

文章目录: 一:运行效果 1.演示 2.思路和功能 二:代码 文件架构 Demo 必备知识:python图形化编程pygame游戏模块 一:运行效果 1.演示 效果图◕‿◕✌✌✌ Python利用pygame实现飞机大战游戏运行演示 参考&#x…

web集群-lvs-DR模式基本配置

目录 环境: 一、配置RS 1、安装常见软件 2、配置web服务 3、添加vip 4、arp抑制 二、配置LVS 1、添加vip 2、安装配置工具 3、配置DR 三、测试 四、脚本方式配置 1、LVS-DR 2、LVS-RS 环境: master lvs 192.168.80.161 no…

YOLOv5改进系列:新的颈部Eff-QAFPN(Efficientrep)结构助力涨点

一、论文理论 本文提出一种硬件友好的卷积神经网络结构,该结构类似于repvgg。在衡量网络效率时,经常使用Flops或者参数量,这些衡量指标对于硬件计算能力和内存带宽不敏感。因此,如何设计一个神经网络架构,使其有效地利用硬件计算能力和内存带宽是至关重要的。 论文地址:E…

Docker如何端口映射?

Docker是一种流行的开源容器化平台,它允许开发者将应用程序和其依赖资源打包到一个称为容器的可移植单元中。Docker提供了强大的管理和部署工具,使得应用程序可以在不同的环境中运行,无需担心环境配置的问题。在使用Docker部署应用程序时&…

【动态规划】【同余前缀和】【多重背包】[推荐]2902. 和带限制的子多重集合的数目

本文涉及知识点 动态规划汇总 C算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 C算法:滑动窗口总结 多重背包 LeetCode2902. 和带限制的子多重集合的数目 给你一个下标从 0 开始的非负整数数组 nums 和两个整数 l 和 r 。 请你…

golang常用库之-golang常用库之-ladon包 | 基于策略的访问控制

文章目录 golang常用库之-ladon包 | 基于策略的访问控制概念使用策略 条件 Conditions自定义conditionLadon Condition使用示例 持久化访问控制(Warden) 结合 Gin 开发一个简易 ACL 接口参考 golang常用库之-ladon包 | 基于策略的访问控制 https://github.com/ory/ladon Lado…