openGauss源码解析(95)

openGauss源码解析:SQL引擎源解析(10)

4. 代价估算

查询执行的代价分为I/O代价和CPU代价。这两种代价都与查询过程中所处理的元组数量正相关。因此,通过选择率对查询计划的总代价进行评估是较为准确的。但由于硬件环境的差别,openGauss的代价模型输出的“代价”只是一种度量计划好坏的通用指标,而不是指执行时间。为描述度量的过程,下面将从代价模型参数的介绍为切入点,逐一展开I/O和CPU的代价评估方法。

(1) I/O代价评估。

在磁盘上,元组是按数据页的方式进行组织的。页的存取方式主要有:顺序读和随机读。受制于存储介质的性能,顺序读的效率明显高于随机读。比如:机械硬盘面临大量随机访问时,磁头寻道的时间占据了数据读取的大部分时间。在openGauss中,不同存取方式I/O代价如下所示:

DEFAULT_SEQ_PAGE_COST 1.0

DEFAULT_RANDOM_PAGE_COST 4.0

默认参数将数据页“顺序读”和“随机读”的开销设置为了1:4。

设置对于机械磁盘来说是比较合理的。但对于寻址能力出众的SSD盘来说,该参数就需要根据具体情况进行调整了。此外,现实中的数据库部署十分复杂,一个系统中可能同时存在多种不同的存储介质。为使得代价模型能同时应对不同存储介质的I/O性能,openGauss给用户提供了设定文件I/O单位代价的方法。

CREATE TABLESPACE TEST_SPC LOCATION '...' WITH (SEQ_PAGE_COST=2, RANDOM_PAGE_COST=3);

根据I/O代价参数和选择率,可以很容易对候选计划的I/O开销进行评估。下面将以顺序扫描(SeqScan)和索引扫描(IndexScan)为例,讲解代价评估的具体过程。

① SeqScan:对表的数据进行从头至尾的遍历,属于顺序读。因此,SeqScan的IO代价为:表数据页总数×DEFAULT_SEQ_PAGE_COST。

② IndexScan:通过索引查找满足约束的表数据,属于随机读。因此,IndexScan的I/O代价为:P * DEFAULT_RANDOM_PAGE_COST。

其中,P(满足数据页数量)与R(满足约束的元组数量)正相关,且R = 表元组总量×选择率;openGauss计算出R后,将调用index_pages_fetched(R, ...)函数估算P。该函数在costsize.c文件中实现,具体细节请参考Mackert L F和Lohman G M的论文Index scans using a finite LRU buffer: A validated I/O model。

通过观察代价模型可以发现当选择率超过一定阈值时,P会相对较大,而使得IndexScan的代价超过SeqScan。因此说明,IndexScan的效率并不总高于SeqScan。

(2) CPU代价评估。

数据库在数据寻址和数据加工阶段都需要消耗CPU资源,比如:元组投影选择、索引查找等。显然,针对不同的操作,CPU花费的代价都是不相同的。openGauss将CPU代价细分为:元组处理代价和数据操作代价。

① 元组处理代价:将一条磁盘数据转换成元组形式的代价;针对普通表数据和索引数据,代价参数分别如下:

#define DEFAULT_CPU_TUPLE_COST 0.01

#define DEFAULT_CPU_INDEX_TUPLE_COST 0.005

默认参数中,索引代价更低。这是因为索引数据所涉及的列一般少于表数据,所需的CPU资源相对较小。

② 数据操作代价:对元组进行投影,或根据约束表达式判断元组是否满足条件的代价。代价参数如下:

#define DEFAULT_CPU_OPERATOR_COST 0.0025

给定以上参数,CPU的代价估算与问题的计算规模成正比,而问题的计算规模取决于选择率。这种关系类似于算法例复杂度与n的关系。限于篇幅,本节不做具体介绍。

posted @ 2024-04-30 10:11  openGauss-bot  阅读(7)  评论(0编辑  收藏  举报