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,其内部使用就是共享内存和页面对应的部分。

相关推荐
weixin_580614002 小时前
如何提取SQL日期中的年份_使用YEAR或EXTRACT函数
jvm·数据库·python
2301_813599552 小时前
SQL生产环境规范_数据库使用最佳实践
jvm·数据库·python
a9511416422 小时前
Go 中通过 channel 传递切片时的数据竞争与深拷贝解决方案
jvm·数据库·python
qq_189807032 小时前
如何修改RAC数据库名_NID工具在集群环境下的改名步骤
jvm·数据库·python
aXin_ya2 小时前
Redis 高级篇(最佳实践)
数据库·redis·缓存
zhangchaoxies2 小时前
如何检测SQL注入风险_利用模糊测试技术发现漏洞
jvm·数据库·python
zhangchaoxies3 小时前
CSS如何实现响应式弹性网格布局_配合media query修改flex-wrap属性
jvm·数据库·python
霖霖总总3 小时前
[Redis小技巧32]Redis分布式锁的至暗时刻:从原理演进到时钟跳跃的终极博弈
数据库·redis·分布式
Polar__Star4 小时前
C#怎么操作Chart图表控件 C#如何用WinForms Chart控件绑定数据绘制统计图表【控件】
jvm·数据库·python
2401_897190554 小时前
CSS如何制作数字滚动效果_利用transform位移数字
jvm·数据库·python