PostgreSQL技术内幕8:PostgreSQL查询执行器

0.简介

执行器是查询编译和存储引擎之间的连接模块,其负责将优化器输出的执行计划,进行初始化、执行,访问存储引擎并获得最终结果返回,本章主要介绍PG的执行器模型和其执行流程。

执行器的处理模型

常见的执行器的处理模型包含基于拉操作的Pull模型和基于推操作的Push模型。

1.1 火山模型(Pull模型)

火山模型也叫迭代器模型,最早是《Volcano, an Extensible and Parallel Query Evaluation System》中提出,其产生的背景是当时的 IO 速度是远远小于 CPU 的计算速度的,所以降低虚函数开销带来的优化微乎其微,且内存空间有限,单个处理更附和当时的场景。火山模型是由上游节点主动pull来驱动下层节点,逐层调用来实现数据的处理。其优缺点也比较明确:

优点:

1)实现简单,通用性好:每个Operator都可以独立实现,不受其他Operator的影响,且不受数据规模限制,可以处理任意规模的数据集。

2)灵活性高:可以灵活控制输出的数量,比如Limit算子及时短路。

缺点:

1)虚函数开销:每次调用GetNext获取一个tuple,会产生大量虚函数调用开销。

2)对应Cache不友好:过多的控制语句和函数调用容易导致缓存失效。

1.2 Push模型

可以看到,Push模型和Pull模型刚好相反,是从底层元组主动向上传递从而驱动整个流程。Push模型在计划的叶子节点开始执行,每层执行完成后物化然后传递给上一层节点。

优点:

1)减少函数调用:与Pull模型相比,Push模型显著减少了函数调用次数。

2)Cache命中率高:由于内部处理逻辑一致,Cache命中率得到显著提升。

缺点:

1)内存占用较大:由于每个节点都需要物化处理后的数据,可能导致内存占用升高。

1.3 向量化执行引擎

可以看到,把pull模型一次获取一条改为一个循环,处理完再向上驱动即为Push模型。向量化是对其另一种优化,一次处理一批数据,减少函数调用次数和缓存切换频率,提高执行效率。同时,结合了列式存储和SIMD指令,提高执行器性能。

2. PG执行器

复制代码
执行器是执行计划和存储引擎之间的关联模块,那么接下来就分别从执行器流程、与执行计划的关联、与存储引擎的关联来进行执行器的分析。

2.1 执行器本身流程

在PG中有四个个用于调用执行器的接口,他们是ExecutorStart、ExecutorRun、ExecutorFinish和ExecutorEnd。其职责如下:

1)ExecutorStart:主要负责初始化各个算子的状态,通过调用standard_ExecutorStart对执行器进行必要的初始化

2)ExecutorRun:执行器运行阶段,通过ExecutorRun来执行算子。

3)ExecutorFinish:统计信息收集和清理。

4)ExecutorEnd:逐层结束下游节点的执行,释放资源。

顺序关系即为:ExecutorStart --> ExecutorRun --> ExecutorFinish -->ExecutorEnd

2.2 执行器与执行计划的关联

与传统执行器直接关联执行计划不同,PG引入了Portal层,负责将查询计划转发,同时根据策略生成路径,其结构如下:

cpp 复制代码
typedef struct PortalData
{
  /* Bookkeeping data */
  const char *name;      /* portal's name */
  const char *prepStmtName;  /* source prepared statement (NULL if none) */
  MemoryContext portalContext;  /* subsidiary memory for portal */
  ResourceOwner resowner;    /* resources owned by portal */
  void    (*cleanup) (Portal portal); /* cleanup hook */

  ....    /* other */
}      PortalData;

Portal提供了三个方法:PortalStart、PortalRun和PortalDrop。

1)PortalStart:初始化Portal参数和策略。

2)PortalRun:根据语句类型选择执行器路径,返回结果。

3)PortalDrop:结束执行器,释放资源。

2.3 执行器和存储引擎的关联

以一个简单的Scan为例,顺序扫描的入口函数为SeqNext,其会调用heap_getnext,heap_getnext内部调用heapgettup,其内部使用就是共享内存和页面对应的部分。

相关推荐
cqsztech33 分钟前
Oracle ai database 26ai rac 通过gold image 更新季度补
数据库·人工智能·oracle
认真就输DBA38 分钟前
一个DBA,把用了16年的两款软件换了
数据库·ai·dba·tabby·笔记软件·obsidian
穆利堂-movno144 分钟前
郑州新网软件科技有限公司AI 数据源错误纠正反馈
大数据·数据库·人工智能
程序员夏洛1 小时前
Redis 数据过期后的删除策略是什么?
数据库·redis·缓存
鱼鳞_1 小时前
热门八股-Redis
数据库·redis·缓存
这个DBA有点耶2 小时前
SQL:2023新特性详解:JSON_TABLE、GREATEST/LEAST、QUALIFY 到底怎么用?
数据库·sql·mysql
AOI小白新手上路2 小时前
江科协15-2 直流电机实验调试总结
数据库·mongodb
小王C语言2 小时前
MySQL 用户管理:创建用户、给用户授权
数据库·mysql
程序员夏洛2 小时前
Redis 的持久化机制有哪些?
java·数据库·redis
l1t2 小时前
DeepSeek总结的修复pgrust v0.2 #83:generate_series 聚合及其底层的两个成本 - #84
数据库·postgresql