PostgreSQL执行器之Materialize

1. Materialize 简述

Materialize 是 PostgreSQL 执行计划的一个组成部分,用于生成一个可复用的临时数据集。

来看一个例子,表 t1 与 t2 关联查询,生成的执行计划如下:

postgres=# select * from t1;
 c1 | c2 | c3
----+----+----
  1 |  1 |  1
  2 |  2 |  2
(2 rows)

postgres=# select * from t2;
 c10 | c20 | c30
-----+-----+-----
   3 |   3 |   3
   6 |   6 |   6
(2 rows)

postgres=# explain select * from t1,t2 where t1.c1=t2.c10;
                            QUERY PLAN
-------------------------------------------------------------------
 Nested Loop  (cost=0.00..62489.90 rows=20808 width=24)
   Join Filter: (t1.c1 = t2.c10)
   ->  Seq Scan on t1  (cost=0.00..30.40 rows=2040 width=12)
   ->  Materialize  (cost=0.00..40.60 rows=2040 width=12)
         ->  Seq Scan on t2  (cost=0.00..30.40 rows=2040 width=12)
(5 rows)

t1 与 t2 关联查询,最上层的执行计划节点是 Nested Loop,实际上就是两层循环嵌套,外层循环遍历 t1 表中的记录,针对 t1 表中的每一条记录,内部循环遍历 t2 表中的记录,然后判断这两条记录是否满足 join 过滤条件(t1.c1=t2.c10)。

上述过程中对 t2 表的扫描只需要执行一次,将其结果集缓存起来,以便在外层循环 t1 表推进到下一条记录时,直接使用 t2 表的缓存结果集即可,不用再次扫描 t2 表。对 t2 表的结果集缓存的操作就是通过 Materialize 方式实现。

2. Materialize 核心结构体与函数

结构体:

MaterialState 对应 Materialize 节点的执行状态,保存 Materialize 执行过程中的各种状态信息。

MaterialState.eof_underlying 表示其下层执行节点是否已经到达结尾,假设其下层执行节点是一个 SeqScan,表示该表扫描是否已扫描结束。

MaterialState.tuplestorestate 表示数据集临时存储状态,可以存储在内存中,如果超过一定大小,则会存储在文件中。

MaterialState.ss.ps.lefttree 指向其子结点,以上述例子来说,其子结点为 t2 表的 Seq Scan

函数:

  • ExecInitMaterial(),初始化 Materialize 及其子节点
  • ExecMaterial(),执行主函数
  • ExecReScanMaterial(),设置状态用于重新读取 Materialize 缓存的数据集
  • ExecEndMaterial(),结束 Materialize 以及其子结点的执行,释放 tuplestorestate,释放其他资源。

3. Materialize 实现逻辑

ExecMaterial() 是执行主函数,可能会被上层函数多次调用。当该函数第一次被调用时,tuplestorestate 为空,需要调用 tuplestore_begin_heap() 函数为 tuplestorestate 变量做初始化操作。

局部变量 eof_tuplestore 用来标示 tuplestorestate 存储的数据集是否已读取结束,当 tuplestorestate 为 true 并且 eof_underlying 为 false 时,即表示 tuplestorestate 已读取结束并且下层的结点没有读取结束,则会从下层结点继续读取数据。

下层结点读取出来的数据会放入 tuplestorestate 中, 以便外层循环的下一次循环执行时能够复用 tuplestorestate 中的数据。当下层结点读取的数据为空时,表示内层循环执行结束,设置 eof_underlying 为 true,返回 NULL。

当外层循环第二次,第三次等后续执行时,内层循环可以直接复用 tuplestorestate 中的数据集,不用再次执行下层结点(比如扫描表数据),提升性能。

文章评论

0条评论