《PostgreSQL数据库内核分析》之查询执行(六)

举报
毛竹 发表于 2024/02/02 16:40:09 2024/02/02
【摘要】 SQL -> 执行计划 -> 执行器执行查询执行器也是被exec_simple_query调用,只是调用的顺序上查询编译器在前,查询执行器在后。查询执行器实际就是按照执行计划的安排,有机地调用存储过程、索引、并发等模块,按照各种执行计划中节点的实现算法来完成数据的读取或修改的过程。查询执行器有四个主要的子模块:Portal、ProcessUtility、Executor和特定功能子模块部分。...

SQL -> 执行计划 -> 执行器执行

查询执行器也是被exec_simple_query调用,只是调用的顺序上查询编译器在前,查询执行器在后。

查询执行器实际就是按照执行计划的安排,有机地调用存储过程、索引、并发等模块,按照各种执行计划中节点的实现算法来完成数据的读取或修改的过程。

查询执行器有四个主要的子模块:Portal、ProcessUtility、Executor和特定功能子模块部分。由于查询执行器将查询分为两大类别,分别由于ProcessUtility和Executor负责执行,因此查询执行器会首先在Portal模块根据输入执行计划选择相应的处理模块。

6.1查询执行策略

执行流程进入查询执行阶段后,会为每种执行计划选择相应的处理结果,执行相应的处理,最后根据要求返回结果。
执行计划树和非计划执行树

6.1.1可优化语句和数据定义语句

可优化语句(Optimizble statement)和数据定义语句(DDL statement)
可优化语句--DML语句
执行器(Executor)和功能处理器(Utility Processor)
1.执行器处理可优化语句,可优化语句包含一个或多个经过重写和优化的查询计划树,执行器会严格根据计划树进行处理。执行器函数名为ProcessQuery,主要代码在src/backend/executor
2.功能处理器用于处理数据定义语句,可根据不同类别的功能调用相应的处理函数 ProcessUtilty sec/backend/commands

6.1.2四种执行策略

1.PORTAL_ONE_SELECT: 处理select语句,该策略会调用执行器来执行。
2.PORTAL_ONE_RETURINING: 面向update/insert/delete等需要进行元祖操作且需要缓存结果的语句,该策略也是调用执行器执行。
3.PORTAL_UTIL_SELECT: 面向单一数据定义语句,该策略调用功能处理器来执行。
4.PORTAL_MULTI_QUERY: 前三种策略的混合类型,它可以处理多个原子操作。

6.1.3策略选择的实现

执行策略选择器将会使用数据结构PortalData

6.1.4Portal执行的过程

“门户”,所有SQL语句的执行都从一个选择好执行策略的Portal开始
Portal执行过程都必须一次调用PortalStart(初始化)、PortalRun(执行)、PortalDrop(清理)
Portal的创建、初始化、执行及清理过程都在exec_simple_query函数中进行

6.2数据定义语句执行

定义数据模式、函数等的功能性语句 为每一种类型的描述语句调用相应的处理函数
数据定义语句的处理过程简单,其执行流程最终会进入到ProcessUtility处理器,然后执行语句对应的不同处理过程。

6.2.1数据定义语句执行流程

由于ProcessUtility需要处理所有类型的数据定义语句,因此其输入数据结构的类型也是各种各样,每种类型的数据结构表示不同的操作类型。ProcessUtility将通过判断数据结构中NodeTag字段的值来区分各种不同节点,并引导执行流程进入相应的处理函数。

​6.2.2执行实例

表创建函数的主要功能是由heap_create_with_catalog完成,之前的各种操作主要是构造heap_create_with_catalog所需要的参数
WITH子句处理主要完成其中存储相关参数的处理,以便存入pg_class系统表的reloption字段中
BuildDescForRelation主要处理表定义中属性名、类型、非空约束以便构造pg_attribute系统表相关内容

6.2.3主要的功能处理器函数

功能处理器(ProcessUtility)本身只作为入口选择函数,会根据输入的节点类型调用相应的处理过程。

6.3可优化语句执行

可优化语句共同热点是被查询编译器处理后生成查询计划树,由执行器(Executor)处理
该模块对外提供三个接口:ExecutorStart、ExecutorRun和ExecutorEnd,其输入是包含查询计划树的数据结构QueryDesc,输出则是相关执行信息或结果数据。
执行器的三个接口函数都是在Portal的相关函数中调用,分别负责执行器的初始化、执行和清理工作,Portal在处理时也使用了同样的方式,可把资源分配回收工作与执行过程独立开,更是一种很好的资源管理方式
执行器对于查询计划树的处理,最终被转换为针对计划树上每一个节点的处理。每种节点表示一种物理代数(Pyhsical Algebra)操作,PostgreSQL对其进行初始化、处理、清理的过程。节点的处理被设计成需求驱动模式,父节点使用孩子节点提供的数据作为输入,并向其上层节点返回处理结果。实际执行时,从根节点处理,每个节点的执行过程会根据需要自动调用孩子节点的执行过程来获取输入数据(一般为元组),从而层层递归执行,实现整个计划树的遍历执行过程。初始化和清理也采用相同的设计模式,这种设计模式使得节点处理的代码结构简洁统一、语义明确,其实现方式简单有效。

6.3.1物理代数与处理模型

数据库的查询逻辑使用逻辑代数(如关系代数)来表示
查询计划树上的节点就构成了物理元组到执行结果的管道,因此查询计划树的执行过程可以看成是拉动元组穿过管道的过程 PG采用一次一元组的执行模式,每个节点被执行一次仅向上层节点返回一条元组。因此,对于整个查询计划树的执行也是一次一元组的模式。优点:
1.减少了返回元组的延迟
2.对于某些操作(如游标、LIMIT子句等)不需要一次性获取所有的元组,节省了开销
3.减少了实现过程中缓存结果带来的代码复杂性和执行过程中临时存储的开销

6.3.2物理操作符的数据结构

PG系统中将所有的计划节点按功能分为四类:控制节点(control node)、扫描节点(scan node)、连接节点(join node)、物化节点(materializaiton node)
执行器的输入是QueryDesc,包含了存储查询计划树根节点指针的PlannedStmt结构。执行器执行时,首先构造全局状态记录Estat结构,并为每个计划节点(Plan)构造对应的状态节点(PlanState),然后再执行中使用相关结构存储执行状态,执行完毕后释放相关的数据结构。

6.3.3执行器的执行

调用执行器分别为:ExecutorStart、ExecutorRun和ExecutorEnd
ExecutorStart———standard_ExecutorStart
ExecutorRun———standard_ExecutorRun
ExecutorEnd———standard_ExecutorEnd
通过调用ExecEndPlan处理状态树根节点释放已分配的资源,最后释放执行器全局状态Estate完成整个执行过程。

6.3.4执行实例

1.分析和重写分析树
2.生成执行计划
3.创建Portal数据结构
4.调用Portal初始化过程
5.调用Portal执行过程
6.调用Portal清理过程

6.4计划节点

PostgreSQL中,计划节点分为四类,分别是控制节点(Control Node)、扫描节点(Scan Node)、物化节点(Materializtion Node)、连接节点(Join Node)
1.控制节点:处理特殊情况的节点,用于实现特殊的执行流程,如Result节点可用来表示INSERT语句中VALUES子句指定的将要插入的元组
2.扫描节点:用于扫描表等对象以从中获取元组。SeqScan
3.物化节点:能缓存执行结果到辅助存储中,物化节点会在第一次被执行时生成其中的所有结果元组,然后将这些结果元组缓存起来,等待其上层节点取用;而非物化节点则是每次被执行时生成一个结果元组并返回给上层节点。
4.连接节点:对应于关系代数中的连接操作。可以实现多种连接方式(条件连接、左连接、右链接、全连接、自然连接等),每种节点实现一种连接算法。如,HashJoin实现了基于Hash的连接算法。

6.4.1控制节点

6.4.2扫描节点

扫描表,每次获取一条元组作为上层节点的输入。扫描节点普遍存在于查询计划树的叶子节点,不仅可以扫描表,还可以扫描函数的结果集、链表结构、子查询结果集等。

6.4.3物化节点

缓存元组的节点
1.Material节点 缓存子节点结果,对于需要重复多次扫描的子节点可以减少执行的代价。其实现方式在于将结果元组存储于特殊的数据结构Tuplesstorestate
2.Sort节点 对下层节点的输出结果进行排序,该节点只有左子节点。
3.Group节点 处理GROUP BY子句,将下层节点满足选择条件(HAVING子句)的元组分组后,只返回该元组的第一个元组
4.Agg节点 执行含由聚集函数的GROUP BY操作,该节点能够实现三种执行策略:Plain(不分组的聚集计算)、Sorted(下层节点提供排好序的元组,类似Group的分组方法,然后进行聚集计算)、Hash(首先对下层节点提供的未排序的元组进行分组,然后进行计算)
5.Unique节点 对下层节点返回的已排序的元组进行去重操作。
6.Hash节点 作为HashJoin节点的辅助节点,共同完成Hash连接方法
7.SetOp节点 处理集合操作,对应于SQL语句中的EXCEPT、INTERSECT两种集合操作,至于另一种集合操作UNION,可直接由Append节点来实现。
SetOp有两种执行策略:排序(SETOP_SORTED)和Hash(SETOP_HASHED)
8.Limit节点 处理LIMIT/OFFSET子句
9.WindowAgg节点 用于处理窗口函数,用于在于当前元组相关的一组元组上执行相关函数计算

6.4.4连接节点

1.Inner Join
2.Left Outer Join
3.Right Outer Join
4.Full Outer Join
5.Semi Join: 类似IN操作
6.Anti Join:类似于NOT IN操作
1.NestLoop节点
2.MergeJoin节点
3.HashJoin节点

6.5其他子功能介绍

6.5.1元组操作

元组存储所有信息,包括各种系统信息、数据等
存储模块提供了很好的元组(HeapTuple)定义和操作接口,但该结构是面向物理元组的,结构解析和构造开销很大,不能满足执行器高效处理元组的需求。

6.5.2表达式计算

处理SQL语句中的函数调用、计算式和条件表达式时需要用到表达式计算。
PG实现了表达式计算子系统封,用于表示和执行SQL语句中的各种表达式。
表达式的计算过程分为三个部分:初始化、执行和清理。
初始化----ExecInitExpr
执行----ExecEvalExpr
清理----释放内存上下文实现
6.5.3投影操作
通过表达式实现,是一种属性过滤过程,该操作将对元组的属性进行精简,把那些在上层计划中不需要用到的属性从元组中去掉,从而构造一个精简版的元组。投影属性

6.6小结

用户输入SQL语句 -》优化器 可优化语句生成计划树 -》 Portal通过判断选择Executor来处理
数据描述语句则在执行过程中由Portal使其统一进入ProcessUtility过程进行执行
执行计划(外层调用Portal接口) -》Portal (操作类型/链表长度)确定执行过程 -》 简单:直接调用Executor;缓存:增加缓存结构和输出结果;更复杂:通用的复杂处理流程

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。