Postgresql源码(133)优化器动态规划生成连接路径的实例分析

  • 物理算子的生成分为两步,基表的扫描路径生成set_base_rel_pathlists;连接路径生成(make_rel_from_joinlist动态规划)。本篇简单分析实现。
  • 看过代码会发现,“基表的扫描路径生成”其实就是作为连接路径生成dp计算的第一层数据,然后逐层拼接上新的连接节点,每层选一个局部最优的 在留几个有序的,就进入到下一层计算。一般有几张表相连就会有几层计算。
  • 入口函数:make_one_rel

1 物理算子基表扫描路径生成set_base_rel_pathlists

  • set_base_rel_sizes
    • 为查询计划中每个基本关系估计大小;预估的行数、行宽;决定是否并行。
  • set_base_rel_pathlists
    • 为查询计划中每个基本关系找到所有可用的扫描路径。包括顺序扫描、索引扫描。识别出所有扫描路径将它们附加到对应基本关系的 pathlist 字段中。

生成基础关系的path:set_base_rel_pathlists,执行后生成的PATH在RelOptInfo数组中保存:

(gdb) p *root->simple_rel_array[1]->pathlist
$37 = {type = T_List, length = 2, max_length = 5, elements = 0x2a2aa40, initial_elements = 0x2a2aa40}

RelOptInfo数组和RTE数组是对应的:

(gdb) p root->simple_rte_array[1]->relid
$40 = 16471

生成结果实例

在这里插入图片描述

2 物理算子连接路径生成make_rel_from_joinlist

经过set_base_rel_pathlists生成扫描路径,每个基表的RelOptInfo都记录了若干条path,这些基表作为扫描的基础节点,再次基础上继续构造连接物理算子。

standard_join_search用动态规划方法来尝试不同的连接顺序和组合:

  • 初始化:从initial_rels提供的初始关系开始,dp的起点。
  • 逐层连接:每一层都会尝试将现有的连接关系与另一个关系结合,形成新的连接关系。
  • 搜索连接顺序:对于每一对可能的连接关系,函数会考虑所有可能的连接方法(如嵌套循环连接、散列连接等),生成一个或多个path。
  • 评估和选择:每个生成的path都会评估成本,优化器会选择成本最低的path作为该连接步骤的最佳路径。
  • 最终结果:返回将所有原始关系连接在一起的结果。
make_rel_from_joinlist

standard_join_search
	...
	// levels_needed = 3 三张表
	root->join_rel_level = (List **) palloc0((levels_needed + 1) * sizeof(List *));
  • 这里是第一层,有三个RelOptInfo(student、course、teacher)
	root->join_rel_level[1] = initial_rels;

  • 从第2层开始处理:
	for (lev = 2; lev <= levels_needed; lev++)
	{
		ListCell   *lc;

  • 搜索一个层级,拿到的所有RelOptInfo可能得组合,把可能的组合放到root->join_rel_level[lev]中。
  • join_rel_level[lev]记录的是RelOptInfo指针,每个RelOptInfo表示一个关系,一个关系可能带多个path。
		join_search_one_level(root, lev);


  • 在连接搜索的一个层级完成后,为每个连接关系生成额外的路径(如分区连接路径和聚合路径),并确定每个连接关系成本最低路径:
		foreach(lc, root->join_rel_level[lev])
		{
			rel = (RelOptInfo *) lfirst(lc);

			generate_partitionwise_join_paths(root, rel);

			if (!bms_equal(rel->relids, root->all_query_rels))
				generate_useful_gather_paths(root, rel, false);

			/* Find and save the cheapest paths for this rel */
			set_cheapest(rel);
		}
	}

三层路径生成实例

  • 第一层有三张基表的RelOptInfo
    • RelOptInfo
      • path : student(seq)
      • path : student(idx)
    • RelOptInfo
      • path : score(seq)
      • path : score(索引)
      • path : score(覆盖索引)
    • RelOptInfo
      • path : course(seq)
      • path : course(索引)
      • path : course(覆盖索引)
    • 虽然seq的代价最小,但第一层也保留了索引扫的path,因为本层代价最小的情况不一定是全局代价最小的情况,还需要保留一些有序的path方便后面节点使用。
  • 第二层返回了两个RelOptInfo,里面的路径都是两张表拼接的结果
    • RelOptInfo
      • path : Hash(score(seq), student(seq))
    • RelOptInfo
      • path : Hash(score(seq), course(seq)) <<<< 第三层选择
      • path : Nestloop(score(索引), Material(course(seq)))
      • path : Nestloop(score(覆盖索引), Material(course(seq)))
  • 第三层返回了一个RelOptInfo,代表最终路径
    • RelOptInfo
      • path : Hash(Hash(score(seq), student(seq)),course(seq))

在这里插入图片描述

3 实例

drop table student;
create table student(sno int primary key, sname varchar(10), ssex int);
insert into student values(1, 'stu1', 0);
insert into student values(2, 'stu2', 1);
insert into student values(3, 'stu3', 1);
insert into student values(4, 'stu4', 0);

drop table course;
create table course(cno int primary key, cname varchar(10), tno int);
insert into course values(1, 'meth', 10);
insert into course values(2, 'english', 11);

drop table teacher;
create table teacher(tno int primary key, tname varchar(10), tsex int);
insert into teacher values(10, 'te1', 1);
insert into teacher values(11, 'te2', 0);

drop table score;
create table score (sno int, cno int, degree int);
create index idx_score_sno on score(sno);
insert into score values (1, 10, 100);
insert into score values (1, 11, 89);
insert into score values (2, 10, 99);
insert into score values (2, 11, 90);
insert into score values (3, 10, 87);
insert into score values (3, 11, 20);
insert into score values (4, 10, 60);
insert into score values (4, 11, 70);


explain 
SELECT * 
FROM STUDENT 
LEFT JOIN SCORE ON STUDENT.sno = SCORE.sno
LEFT JOIN COURSE ON SCORE.cno = COURSE.cno;

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-05-26 06:38:24       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-05-26 06:38:24       106 阅读
  3. 在Django里面运行非项目文件

    2024-05-26 06:38:24       87 阅读
  4. Python语言-面向对象

    2024-05-26 06:38:24       96 阅读

热门阅读

  1. Python库之lxml的简介、安装、使用方法详细攻略

    2024-05-26 06:38:24       41 阅读
  2. [AIGC] CompletableFuture如何实现任务链式调用?

    2024-05-26 06:38:24       35 阅读
  3. HLS入门

    HLS入门

    2024-05-26 06:38:24      37 阅读
  4. 前端调用浏览器录音功能且生成文件(vue)

    2024-05-26 06:38:24       34 阅读
  5. H3CNE-5-IP子网划分(二)

    2024-05-26 06:38:24       33 阅读
  6. 6、设计模式之桥接模式

    2024-05-26 06:38:24       37 阅读
  7. junit测试对应功能,方法使用

    2024-05-26 06:38:24       36 阅读
  8. mysql-增量备份流程详细流程

    2024-05-26 06:38:24       39 阅读
  9. 使用Python提取PDF中的文本和表格数据

    2024-05-26 06:38:24       44 阅读
  10. 数据库简介

    2024-05-26 06:38:24       34 阅读
  11. 洛谷 P3803 【模板】多项式乘法(FFT)

    2024-05-26 06:38:24       41 阅读