一. 前言
在OepnGuass中,一条路径的执行代价估算值将直接决定一条路径是否会被取舍。本文主要对OpenGuass中对于普通表的顺序扫描和索引扫描两种路径的的代价估算进行代码走读了解代价估算的整体过程。
二. 顺序扫描代价估算
顺序扫描的路径代价估算在OpenGuass中实现还是比较简单的,其实现是在函数cost_seqscan中实现的,其计算公式为:
顺序扫描的代价 = 顺序扫描Page的数量 * 每个顺序扫描Page的代价 / dop + 表的总行数 * 每个元组的处理代价
上述各元素的获取:
顺序扫描Page的数量: pg_class中的relpages 字段值
顺序扫描Page的代价 : 默认为1
dop: 路径并发度
表的总行数: 路径的行数,大小为pg_class的reltuples字段值
每个元组的处理代价: 默认为0.0125
三. 索引扫描代价估算
索引扫描的代价估算较为复杂,其只要是在cost_index函数实现的,其实只要核心的计算方法是在btcostestimate_internal的方法中,其计算公司如下所示:
索引扫描的代价 = 扫描Page的数量 * 扫描Page的代价 + 元组数量总数 * 谓词过滤率 * 每个元组的处理代价 + 谓词中数组的长度* 100 * 每次CPU操作的代价 + IO的代价估算
上述中各元素获取如下:
扫描Page的数量 : pg_class系统表的的relpages字段中值
扫描Page的代价 : 索引扫描Page的代价默认为4
元组数量总数: pg_class的reltuples字段的值
谓词过滤率 : 在函数clauselist_selectivity中估算
每个元素的处理代价:默认为(0.0025 + 0.005)
谓词中数组的长度: 如果谓词中有数组的场景,则为数组的长度,比如 where id = any(array[1, 2, 3]) 为3, 如果没有数组,则为1
每次CPU操作的代价: 默认为0.0025
IO的代价估算:max_IO_cost + csquared * (min_IO_cost - max_IO_cost)
其中: max_IO_cost : 估算最大扫描的索引的IO代价,通过一个较复杂的计算公式算出最大扫描的索引的页面数量 * 扫描Page的代价,计算公司参考index_pages_fetched
csquared :索引相关性,一般为1
min_IO_cost :估算最小扫描的索引的IO代价,索引的页面数 * 谓词过滤率 * 扫描Page的代价
四. 两种路径代价对比
从上边的计算公式也可以看到,虽然索引扫描的路径代价会被谓词过滤率过滤掉一部分数据,但是也增加了一些索引Page的扫描开销,因此实际上索引扫描的代价未必会比顺序扫描小,比如如下场景就因为顺序扫描代价较小从未选择的顺序扫描执行: