在单表查询的例子中,执行器从计划树中取出计划节点,按照自底向上的顺序进行处理,并调用节点相应的处理函数。
每个计划节点都有相应的函数,用于执行节点对应的操作。这些函数在src/backend/executor
目录中。例如,执行顺序扫描的的函数(SeqScan
)定义于nodeSeqscan.c
中;执行索引扫描的函数(IndexScanNode
)定义在nodeIndexScan.c
中;SortNode
节点对应的排序函数定义在nodeSort.c
中,诸如此类。
当然,理解执行器如何工作的最好方式,就是阅读EXPLAIN
命令的输出。因为PostgreSQL的EXPLAIN
命令几乎就是照着计划树输出的。下面以 3.3.3 节 的例1为例。
testdb=# EXPLAIN SELECT * FROM tbl_1 WHERE id < 300 ORDER BY data;
QUERY PLAN
---------------------------------------------------------------
Sort (cost=182.34..183.09 rows=300 width=8)
Sort Key: data
-> Seq Scan on tbl_1 (cost=0.00..170.00 rows=300 width=8)
Filter: (id < 300)
(4 rows)
我们可以自底向上阅读EXPLAIN
的结果,来看一看执行器是如何工作的。
第6行 :首先,执行器通过nodeSeqscan.c
中定义的函数执行顺序扫描操作。 第4行 :然后,执行器通过nodeSort.c
中定义的函数,对顺序扫描的结果进行排序。
临时文件
执行器在处理查询时会使用工作内存(
work_mem
)和临时缓冲区(temp_buffers
),两者都于内存中分配。如果查询无法在内存中完成,就会用到临时文件。使用带有
Analyze
选项的EXPLAIN
,待解释的命令会真正执行,并显示实际结果行数,实际执行时间和实际内存用量。下面是一个具体的例子:testdb=# EXPLAIN ANALYZE SELECT id, data FROM tbl_25m ORDER BY id; QUERY PLAN ---------------------------------------------------------------------- Sort (cost=3944070.01..3945895.01 rows=730000 width=4104) (actual time=885.648..1033.746 rows=730000 loops=1) Sort Key: id Sort Method: external sort Disk: 10000kB -> Seq Scan on tbl_25m (cost=0.00..10531.00 rows=730000 width=4104) (actual time=0.024..102.548 rows=730000 loops=1) Planning time: 1.548 ms Execution time: 1109.571 ms (6 rows)
在第6行,
EXPLAIN
命令显示出执行器使用了10000KB的临时文件。临时文件会被临时创建于
base/pg_tmp
子目录中,并遵循如下命名规则{"pgsql_tmp"} + {创建本文件的Postgres进程PID} . {从0开始的序列号}
比如,临时文件
pgsql_tmp8903.5
是pid
为8903
的postgres
进程创建的第6个临时文件
下一节:PostgreSQL 中支持三种连接(JOIN)操作:嵌套循环连接(Nested Loop Join),归并连接(Merge Join) ,散列连接(Hash Join)。在PostgreSQL中,嵌套循环连接与归并连接有几种变体。