PostgreSQL代价模型

举报
宁谷花雨 发表于 2022/04/22 17:28:38 2022/04/22
【摘要】 一、代价1、基于页面的IO基准代价#define DEFAULT_SEQ_PAGE_COST             1. 0#define DEFAULT_RANDOM_PAGE_COST 4. 0 2、基于元组的CPU基准代价#define DEFAULT_CPU_TUPLE_COST                0.01#define DEFAULT_CPU_INDEX_TUPLE ...

一、代价

1、基于页面的IO基准代价

#define DEFAULT_SEQ_PAGE_COST             1. 0

#define DEFAULT_RANDOM_PAGE_COST 4. 0

 

2、基于元组的CPU基准代价

#define DEFAULT_CPU_TUPLE_COST                0.01

#define DEFAULT_CPU_INDEX_TUPLE COST 0.005

定义 DEFAULT_CPU_TUPLE_COST 来表示处理一条元组的代价,使用DEFAULT_CPU_INDEX_TUPLE_COST 来表示处理一条索引元组的代价。

 

3、基于表达是的CPU基准代价

#define DEFAULT_CPU_OPERATOR_COST    0.0025

执行计划的过程中,不止处理元组需要消耗 CPU 资源,在投影、约束条件中包含大量的表达式,对这些表达式求值同样需要消耗 CPU 资源,因此 PostgreSQL 数据库把表达式的求值代价单独剥离出来。使用DEFAULT_CPU_OPERATOR_COST来作为计算表达式代价的基准单位,用户可以通过调整 cpu_operator_cost 来调整这个基准单位。

 

4、并行查询产生的基准代价

目前, PostgreSQL 数据库部分支持了并行查询,因此通常在分布式数据库系统中才考虑的通信代价目前 PostgreSQL 数据库也需要考虑了,因为 Gather 进程和 Worker 进程在并行查询的过程中需要进行通信,因此需要考虑进程间通信(IPC)所需的初始化代价( DEFAULT_PARALLEL_SETUP_COST ),以及 Worker 进程向 Gather 进程投递元组的代价( DEFAULT_PARALLEL_TUPLE_COST )。

#define DEFAULT_PARALLEL_TUPLE_COST   0.1

#define DEFAULT_PARALLEL_SETUP_COST   1000.0

 

double parallel_tuple_cost = DEFAULT_PARALLEL_TUPLE_COST;

double parallel_setup_cost = DEFAULT_PARALLEL_SETUP_COST;

 

5、缓存对代价的影晌

数据库本身有缓存系统,磁盘上也有磁盘缓存,当读取一个缓存中的数据页面时是不会产生磁盘 IO 的,因此,如果对每个页面都计算磁盘 IO 的代价,代价的计算结果就会失真,所以我们还需要对缓存中的页面数量有一个估计,目前 PostgreSQL 数据库用 effective_cache_size参数来表示,实际上这个值一定是不准确的,这是 PostgreSQL 数据库需要改进的地方。

#define DEFAULT_EFFECTIVE_CACHE_SIZE 524288

int effective_cache_size = DEFAULT_EFFECTIVE_CACHE_SIZE;

 

6、启动代价和整体代价

PostgreSQL 数据库将代价分成了两个部分:启动代价( Startup Cost )和执行代价( Run Cost), 两者的和是整体代价( Total Cost )。

Total Cost= Startup Cost+ Run Cost

Path 结构体中用 startup_cost 和 total_cost 两个变量来表示启动代价和整体代价,startup_cost 是指从语句开始执行到查询引擎返回第一条元组的代价(另一种说法是准备好获得第一条元组的代价) , total_cost 是SQL 语句从开始执行到结束的所有代价。

 

7、表达式代价的计算

表达式代价的基准单位是 cpu_operator_cost ,不同的表达式需要辅以基准单位进行计算,表达式代价主要包括如下方面:

  • 对投影列的表达式进行计算产生的代价
  • 对约束条件中的表达式进行计算产生的代价

.对函数参数中的表达式进行计算产生的代价

.对聚集函数中的表达式进行计算产生的代价。

.子计划等执行计算产生的代价

  

二、路径(Path

  

逻辑优化:

主要是基于 SQL 语句中指定的逻辑运算符进行等价的变换 ,

物理优化:

在逻辑优化的基础之上建立一棵路径树,路径树中的每一个路径都是一个物理算子,这些物理算子是为查询执行器准备的,查询执行器通过运行这些物理算子来实现查询中的逻辑运算,这些物理算子又大体可以分为两类,它们是扫描路径和连接路径。

 扫描路径:

是针对基表而言的,这些基表就是 RELOPT_BASEREL 类型的 RelOptinfo,这些基表是一个二维的关系实体,扫描路径就是对这个二维的关系实体进行遍历的过程,它包括:顺序扫描路径、 (快速)索引扫描路径、位图扫描路径、 TID扫描路径等。

 连接路径:

是记录基表之间的物理连接关系,我们已经将表之间的逻辑连接关系记录到了 SpecialJoinlnfo 结构体中,在物理优化的阶段会根据这些逻辑连接关系建立一个新的RelOptinfo,然后将基于逻辑连接关系建立的物理连接路径记录到这个 RelOptlnfo 中, 并且边记录边筛选。例如针对 InnerJoin 这样一个逻辑连接关系,查询优化器可以建立 NestloopJoin、HashJoin 等物理连接路径,这些物理连接路径都可以实现 InnerJoin 的运算,但是它们的路径代价是不同的,因此物理优化的一个主要工作就是建立物理连接路径并且选出代价最低的物理连接路径。

--------------------

一、

最优的执行路径生成后,虽然 Path Tree 己经足够清楚地指出 查询计划要进行的物理操作,但是它的结构体中为了进行代价计算有太多的冗余信息,不方便查询执行器使用,并且有些参数还没有建立好,因此通过将其转换成执行计划来生成更适合查询执行器的 Plan Tree ,然后将Plan Tree 转交给执行器就可以真正执行了。

路径的结构体是 Path 或者是“继承”自 Path 的新的路径结构体,例如 JoinPath;对应的执行计划节点的结构体是 Plan 或者是“继承”自 Plan 新的执行计划节点,例如 Join 结构体。

PostgreSQL 数据库的每个 Path 节点都一一对应一个 Plan 节点,最优的执行路径需要通过 create_plan 函数转换成对应的执行计划。

 

物理优化阶段己经选出了 代价最低的最优路径,这里通过 create_plan 函数将其中的 Path节点一一转换成为 Plan 节点,转换的过程如图 10-2 所示。

 

二、扫描计划

物化路径要求保持最小的投影结果( CP_SMALL_TLIST ),在 create_plan 函数遍历最优路径中的各个节点的过程中,如果发现诸如 Hash 、Sort、 Materia 之类的节点,会设置 CP_SMALL_TLIST 标志,这样下层路径就不会随便扩展投影列了,因为这时候扩展投影列会带来无谓的代价消耗。

 在生成扫描路径时, 路径节点( Path )都要转换成新的计划节点( Plan), 在分别转换每个路径之前,所有的扫描节点有一些公共的事情要处理,于是通过 creat_scan _plan 函数先处理所有扫描路径转换过程中的公共部分。

 create_scan_plan 函数处理了所有扫描路径公共的特性之后,开始单独处理每个扫描路径向扫描计划的转换。

  

扫描节点上只有过滤条件,这些过滤条件可 通过 RelOptlnfo->baserestrictinfo来获得,但是索引扫描例外,索引结构体 IndexOptlnfo 中通过 indrestrictinfo 也保存了一份过滤条件, indrestrictinfo 通常和 baserestrictinfo 相同, 但是对于带有谓词的局部索引, indrestrictinfo通常和baserestrictinfo 可能是不同的。

 

1、顺序执行计划

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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