概述
在生成执行计划之前,Oceanbase会估算访问路径的代价以剪除代价较大的访问路径,为进一步优化提供基础。
基本原理
在Oceanbase中,主要涉及以下几种路径的代价估算:
基表访问路径
连接路径
子查询路径(不作介绍)
基表访问路径
根据访问方式的不同,基表访问主要可以分为GET操作和SCAN操作。根据查询使用的索引情况,上述两种方式各自需要考虑回表和不回表两种场景,下面依次进行说明。
GET操作
回表场景下,GET操作的基表访问代价可以用如下公式表示:
cost = base_cost (二级索引访问代价)+ ib_cost(主索引访问代价) + network_cost(回表数据网络传输代价)
其中各部分的计算方式如下:
base_cost = io_cost (io代价)+ cpu_cost(cpu代价)
io_cost = MICRO_BLOCK_RND_COST(宏块读取代价常量) * num_micro_blocks_read(需要读取的宏块数) + FETCH_ROW_RND_COST(数据取回代价常量) * row_count(读取的行记录数)
cpu_cost = (CPU_TUPLE_COST(单记录处理代价常量) + PROJECT_COLUMN_RND_COST (列投影代价常量)* column_count(列数)) * row_count (读取的行记录数)+ qual_cost(谓词代价)
ib_cost = io_cost + cpu_cost
network_cost = DEFAULT_NETWORK_PER_BYTE_COST(网络传输代价常量) * (average_row_size_(平均行记录大小) * row_count (传输的行记录数)* column_count(列数)) / table_column_count_(表的列数)
不回表场景下,GET操作的基表访问代价可以用如下公式表示:
cost = base_cost(主索引访问代价)
SCAN操作
回表场景下,SCAN操作的基表访问代价可以用如下公式表示:
cost = base_cost (二级索引访问代价)+ ib_cost(主索引访问代价) + network_cost(回表数据网络传输代价)
其中各部分的计算方式如下:
base_cost = io_cost (io代价)+ cpu_cost(cpu代价)
io_cost = num_micro_blocks_read(需要读取的微块数) * MICRO_BLOCK_SEQ_COST(微块顺序读取代价常量)
cpu_cost = row_count(读取的行记录数) * CPU_TUPLE_COST(单记录处理代价常量)+ project_cost (投影代价)+ qual_cost(谓词代价)
project_cost = (PROJECT_COLUMN_SEQ_COST(投影列顺序处理代价常量) * column_count(列数)) * row_count(读取的行记录数)
不回表场景下,SCAN操作的基表访问代价可以用如下公式表示:
cost = base_cost(主索引访问代价)
连接路径
根据连接方式的不同,优化器会使用不同的逻辑进行代价估算:
Merge Join
op_cost = CPU_TUPLE_COST(单记录处理代价常量) * (left_rows(左表记录数) + right_rows(右表记录数)) + qual_cost(谓词代价) + join_cost(连接代价)
其中各部分的计算方式如下:
join_cost = JOIN_PER_ROW_COST (单记录连接代价常量)* out_tuples(输出记录数)
out_tuples = cond_tuples(满足连接条件的记录数) * filter_selectivity(过滤条件选择率)
cond_tuples = cart_tuples(左右表笛卡尔积的记录数) * cond_selectivity(连接条件选择率)
Hash Join
op_cost = CPU_TUPLE_COST (单记录处理代价常量)* (left_rows(左表记录数) + right_rows(右表记录数)) + material_cost(左表物化代价) + build_cost(hash表构建代价) + probe_cost (hash表probe代价)+ qual_cost (谓词代价)+ join_cost(连接代价)
其中各部分的计算方式如下:
build_cost = BUILD_HASH_PER_ROW_COST(单记录hash表插入代价常量) * left_rows(左表记录数)
probe_cost = PROBE_HASH_PER_ROW_COST (单记录hash表probe代价常量)* right_rows(右表记录数)
join_cost = JOIN_PER_ROW_COST (单记录连接代价常量)* out_tuples(输出记录数)
cond_tuples = left_rows(左表记录数) * right_rows(右表记录数) * cond_sel(连接条件选择率)
Nestloop Join
op_cost = left_rows(左表记录数) * once_rescan_cost(内循环代价) + qual_cost(谓词代价) + join_cost(连接代价)
其中各部分的计算方式如下:
once_rescan_cost = right_cost_(右表代价) + right_rows(右表记录数) *CPU_TUPLE_COST(单记录处理代价常量)
join_cost = JOIN_PER_ROW_COST(单记录连接代价常量) * out_tuples(输出记录数)
out_tuples = cart_tuples (左右表笛卡尔积的记录数)* filter_selectivity(过滤条件选择率)
代码解析
基表访问路径的代价估算逻辑入口为ObOptEstCost::cost_table_one_batch,执行逻辑如下:
调用cost_table_get_one_batch函数对GET操作的访问代价进行估算。
调用cost_table_scan_one_batch函数对SCAN操作的访问代价进行估算。
连接路径的代价估算逻辑根据连接方式的不同,分别位于ObOptEstCost::cost_mergejoin、ObOptEstCost::cost_hashjoin和ObOptEstCost::cost_nestloop。