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

相关推荐
橘猫云计算机设计1 小时前
基于Django的购物商城平台的设计与实现(源码+lw+部署文档+讲解),源码可白嫖!
java·数据库·spring boot·后端·django
2501_903238651 小时前
Spring Boot日志配置与环境切换实战
数据库·spring boot·后端·个人开发
梓沂1 小时前
审计级别未启用扩展模式导致查询 DBA_AUDIT_TRAIL 时 SQL_TEXT 列为空
数据库·sql·dba
Smile丶凉轩1 小时前
数据库面试知识点总结
数据库·c++·mysql
RainbowSea3 小时前
9-1. MySQL 性能分析工具的使用——last\_query\_cost,慢查询日志
数据库·sql·mysql
Sui_Network3 小时前
Sui 如何支持各种类型的 Web3 游戏
大数据·数据库·人工智能·游戏·web3·区块链
m0_748248773 小时前
在 Ubuntu 上安装 Nginx 的详细指南
nginx·ubuntu·postgresql
ZKNOW甄知科技3 小时前
IT服务运营管理体系的常用方法论与实践指南(上)
大数据·数据库·人工智能
黄雪超4 小时前
深入HBase——核心组件
数据结构·数据库·hbase
qq_529835355 小时前
Redis作为缓存和数据库的数据一致性问题
数据库·redis·缓存