执行计划
以如下SQL语句为例:
select
cjxh,
count(1)
from dwcjk
group by cjxh;
执行EXPLAIN的输出为:
执行计划字段解读(横向):
● id:执行算子节点编号。
● operation:具体的执行节点算子名称。
Vector前缀的算子是指向量化执行引擎算子,一般出现含有列存表的Query中。
Streaming是一个特殊的算子,它实现了分布式架构的核心数据shuffle功能,
Streaming共有三种形态,分别对应了分布式结构下不同的数据shuffle功能:
– Streaming (type: GATHER):作用是coordinator从DN收集数据。
– Streaming(type: REDISTRIBUTE):作用是DN根据选定的列把数据重分布到
所有的DN。
– Streaming(type: BROADCAST):作用是把当前DN的数据广播给其他所有的
DN
● E-rows:每个算子估算的输出行数。
● E-memory:DN上每个算子估算的内存使用量,只有DN上执行的算子会显示。某
些场景会在估算的内存使用量后使用括号显示该算子在内存资源充足下可以自动
扩展的内存上限。
● E-width:每个算子输出元组的估算宽度。
● E-costs:每个算子估算的执行代价。
– E-costs是优化器根据成本参数定义的单位来衡量的,习惯上以磁盘页面抓取
为1个单位, 其它开销参数将参照它来设置。
– 每个节点的开销(E-costs值)包括它的所有子节点的开销。
– 开销只反映了优化器关心的东西,并没有把结果行传递给客户端的时间考虑
进去。虽然这个时间可能在实际的总时间里占据相当重要的分量,但是被优
化器忽略了,因为它无法通过修改规划来改变。
执行计划层级解读(纵向):

  1. 第一层:CStore Scan on dwcjk
    表扫描算子,用CStore Scan的方式扫描表dwcjk。这一层的作用是把表dwcjk的数
    据从buffer或者磁盘上读上来输送给上层节点参与计算。
  2. 第二层:Vector Hash Aggregate
    聚合算子,作用是把下层计算输送上来的算子做聚合操作(group by)。
  3. 第三层:Vector Streaming (type: GATHER)
    Shuffle算子,此处GATHER类型的Shuffle算子作用是把数据从DN汇聚到CN。
  4. 第四层:Row Adapter
    存储格式转化算子,主要作用是把内存中列式格式数据转为行式数据,以便客户
    端展示。
    需要注意的是最顶层算子为Data Node Scan时,需要设置
    enable_fast_query_shipping为off才能看到具体的执行计划,如下面这个计划:
    openGauss=# explain select cjxh, count(1) from dwcjk group by cjxh;
    QUERY PLAN

Data Node Scan (cost=0.00…0.00 rows=0 width=0)
Node/s: All datanodes
(2 rows)
设置enable_fast_query_shipping参数之后,执行计划显示如下:

图片.png


执行计划中的关键字说明:

  1. 表访问方式
    – Seq Scan
    全表顺序扫描。
    – Index Scan
    优化器决定使用两步的规划:最底层的规划节点访问一个索引,找出匹配索
    引条件的行的位置,然后上层规划节点真实地从表中抓取出那些行。独立地
    抓取数据行比顺序地读取它们的开销高很多,但是因为并非所有表的页面都
    被访问了,这么做实际上仍然比一次顺序扫描开销要少。使用两层规划的原
    因是,上层规划节点在读取索引标识出来的行位置之前,会先将它们按照物
    理位置排序,这样可以最小化独立抓取的开销。
    如果在WHERE里面使用的好几个字段上都有索引,那么优化器可能会使用索
    引的AND或OR的组合。但是这么做要求访问两个索引,因此与只使用一个索
    引,而把另外一个条件只当作过滤器相比,这个方法未必是更优。
    索引扫描可以分为以下几类,他们之间的差异在于索引的排序机制。
    ▪ Bitmap Index Scan
    使用位图索引抓取数据页。
    ▪ Index Scan using index_name
    使用简单索引搜索,该方式表的数据行是以索引顺序抓取的,这样就令
    读取它们的开销更大,但是这里的行少得可怜,因此对行位置的额外排
    序并不值得。最常见的就是看到这种规划类型只抓取一行,以及那些要
    求ORDER BY条件匹配索引顺序的查询。因为那时候没有多余的排序步
    骤是必要的以满足ORDER BY。
  2. 表连接方式
    – Nested Loop
    嵌套循环,适用于被连接的数据子集较小的查询。在嵌套循环中,外表驱动
    内表,外表返回的每一行都要在内表中检索找到它匹配的行,因此整个查询
    返回的结果集不能太大(不能大于10000),要把返回子集较小的表作为外
    表,而且在内表的连接字段上建议要有索引。
    – (Sonic) Hash Join
    哈希连接,适用于数据量大的表的连接方式。优化器使用两个表中较小的
    表,利用连接键在内存中建立hash表,然后扫描较大的表并探测散列,找到
    与散列匹配的行。Sonic和非Sonic的Hash Join的区别在于所使用hash表结构
    不同,不影响执行的结果集。
    – Merge Join
    归并连接,通常情况下执行性能差于哈希连接。如果源数据已经被排序过,
    在执行融合连接时,并不需要再排序,此时融合连接的性能优于哈希连接。
  3. 运算符
    – sort
    对结果集进行排序。
    – filter
    EXPLAIN输出显示WHERE子句当作一个"filter"条件附属于顺序扫描计划节
    点。这意味着规划节点为它扫描的每一行检查该条件,并且只输出符合条件
    的行。预计的输出行数降低了,因为有WHERE子句。不过,扫描仍将必须访
    问所有 10000 行,因此开销没有降低;实际上它还增加了一些(确切的说,
    通过10000 * cpu_operator_cost)以反映检查WHERE条件的额外CPU时间。
    – LIMIT
    LIMIT限定了执行结果的输出记录数。如果增加了LIMIT,那么不是所有的行
    都会被检索到。
Logo

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。

更多推荐