PostgreSQL7.0手册-开发者手册 -61. PostgreSQL 内部概貌
第六十一章. PostgreSQL 内部概貌
内容
查询的过程
联接是如何建立起来的
分析器阶段
Postgres 规则系统
规划器/优化器
执行器
作者:本章最初是做为 Simkovics, 1998 的一部分出现的,它是 Stefan Simkovics 在 维也纳理工大学准备的硕士论文,是由 O.Univ.Prof.Dr. Georg Gottlob 和 Univ.Ass. Mag. Katrin Seyr 指导的。
本章给出了 Postgres 后端服务器的内部情况的一个概貌。在阅读完毕下面的章节后,你应该对如何处理查询有一个概念了。不过别指望我们在这里有详细的描述(我想,要对Postgres 里面所有的数据结构和函数都做这样的详细描述可能要超过 1000 页的内容!)。本章只是试图帮助我们了解在后端里面从受到查询到发出结果之间的通常的控制和数据的流动。
查询的过程
下面是一个简短的描述一个查询从开始到得到结果要经过的阶段。
首先必须先建立起从应用程序到 Postgres 服务器的联接。应用程序想服务器发送查询然后接收从服务器收到的结果。
分析阶段(parser stage)检查从应用程序(客户端)发送过来的查询,核对语法并创建一个查询树(query tree)。
重写系统(rewrite system)接收分析阶段来的查询树并且搜索任意应用到查询树上的规则(rules)(存储在系统表里)并根据给出的规则体(rule bodies)进行转换。重写系统的一个应用在视图(views)的实现里给出了。
当一个查询访问一个视图时(也就是说,一个虚拟表(virtual table)),重写系统改写用户的查询,使之成为一个访问带有视图定义(view definition)的基本表(base tables)的查询。
规划器/优化器(planner/optimizer)接收(改写的)查询树然后创建一个查询规划(queryplan),这个查询规划是执行器(executor)的输入。
它(规划器/优化器)首先创建所有得出相同结果的可能的 路径(paths)。例如,如果待扫描的关系上有一个索引,那么扫描的路径就有两个。一个可能是简单的顺序查找,而另一个可能就是使用索引的那个。下一步是计算出每个索引执行的开销,并且选择和返回开销最少的那个。
执行器递归地走过规划树(plan tree)并且在这个过程中检索规划所代表的记录。执行器在对关系进行扫描时使用存储系统(storage system),进行排序(sorts)和联接(joins),计算条件(qualifications)并且最终交回生成的记录。
在随后的章节里,我们将对上面的每一个步骤进行更详细的讨论,以便让我们对Postgres 的内部控制和数据结构有一个更准确的理解。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
联接是如何建立起来的
Postgres 是用一个简单的"每用户一进程"的client/server 模型实现的。在这种模式里一个客户端进程只是与一个服务器进程联接。因为我们不知道具体要建立多少个联接,所以我们不得不利用一个主进程 在每次联接请求时派生出一个新的服务器进程来。这个主进程叫做 postmaster,它监听着一个特定的 TCP/IP 端口等待进来的联接。每当检测到一个联接请求时,postmaster 进程派生出一个新的叫 postgres 的服务器进程。服务器任务(postgres 进程)相互之间使用信号灯和共享内存进行通讯,以确保在并行的数据访问过程中的数据完整性。图 \ref{connection} 显示了主进程 postmaster,服务器进程 postgres 和客户端应用之间的相互关系。
客户端进程要么是 psql 前端(用于交互的 SQL 查询)或者是任意用 libpg 库实现的用户应用。请注意用 ecpg(Postgres 用于 C 的嵌入 SQL 预处理器)实现的应用同样也使用这个库。
一旦建立起来一个联接,客户端进程就可以向后端服务器进程发送查询了。查询是通过纯文本传输的,也就是说在前端(客户端)不做任何分析处理。服务器分析查询,创建执行规划,执行该规划并且通过已经建立起来的联接把检索出来的记录返回给客户端。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
分析器阶段
分析器阶段( parser stage )包含两个部分:
在 gram.y 和 scan.l 里定义的分析器是使用 Unix 工具 yacc 和 lex 制作的。
转换处理对分析器返回的数据结构进行修改和增补。
分析器
分析器必须检查(以纯 ASCII 文本方式到来的)查询字串的语法。如果语法正确,则创建一个分析树并将之传回,否则,返回一个错误。在这个实现里我们使用了著名的 Unix 工具 lex 和 yacc。
lexer (词法)在文件 scan.l 里定义,负责识别标识符,SQL 关键字等。对于发现的每个关键字或者标识符都会生成一个记号并且传递给分析器。
分析器在文件 gram.y 里定义并且包含一套语法规则和触发规则时执行的动作。动作代码(实际上是 C 代码)用语建立分析树。
文件 scan.l 用 lex 转换成 C 源文件而 gram.y 用 yacc 转换成 gram.c。在完成这些转换后,一个通用的 C 编译器可以用于创建分析器。千万不要对生成的 C 源文件做修改,因为下一次调用 lex 或 yacc 会把它们覆盖。
注意:上面提到的转换和编译是使用跟随Postgres 发布的 makefiles 自动完成的。
对 yacc 或者 gram.y 里的语法规则的详细描述超出本文的范围。有很多关于lex 和 yacc 的书籍和文档。你在开始研究 gram.y 里给出的语法之前,你应该对 yacc 很熟悉,否则你是看不懂那里面的内容,理解不了发生了什么事情的。
为了更好地理解处理一个查询时 Postgres 里使用的数据结构,我们用一个简单的例子演示在每个阶段数据结构所做的改变。
例 54-1. 一个简单的选择(Select)
这个例子包含下面的简单的查询,这个例子将会在本章剩余各节的所有描述和图示里出现。该查询假设在Supplier Database 数据库里的表已经定义了。
select s.sname, se.pno
from supplier s, sells se
where s.sno > 2 and s.sno = se.sno;
图 \ref{parsetree} 显示了 gram
内容
查询的过程
联接是如何建立起来的
分析器阶段
Postgres 规则系统
规划器/优化器
执行器
作者:本章最初是做为 Simkovics, 1998 的一部分出现的,它是 Stefan Simkovics 在 维也纳理工大学准备的硕士论文,是由 O.Univ.Prof.Dr. Georg Gottlob 和 Univ.Ass. Mag. Katrin Seyr 指导的。
本章给出了 Postgres 后端服务器的内部情况的一个概貌。在阅读完毕下面的章节后,你应该对如何处理查询有一个概念了。不过别指望我们在这里有详细的描述(我想,要对Postgres 里面所有的数据结构和函数都做这样的详细描述可能要超过 1000 页的内容!)。本章只是试图帮助我们了解在后端里面从受到查询到发出结果之间的通常的控制和数据的流动。
查询的过程
下面是一个简短的描述一个查询从开始到得到结果要经过的阶段。
首先必须先建立起从应用程序到 Postgres 服务器的联接。应用程序想服务器发送查询然后接收从服务器收到的结果。
分析阶段(parser stage)检查从应用程序(客户端)发送过来的查询,核对语法并创建一个查询树(query tree)。
重写系统(rewrite system)接收分析阶段来的查询树并且搜索任意应用到查询树上的规则(rules)(存储在系统表里)并根据给出的规则体(rule bodies)进行转换。重写系统的一个应用在视图(views)的实现里给出了。
当一个查询访问一个视图时(也就是说,一个虚拟表(virtual table)),重写系统改写用户的查询,使之成为一个访问带有视图定义(view definition)的基本表(base tables)的查询。
规划器/优化器(planner/optimizer)接收(改写的)查询树然后创建一个查询规划(queryplan),这个查询规划是执行器(executor)的输入。
它(规划器/优化器)首先创建所有得出相同结果的可能的 路径(paths)。例如,如果待扫描的关系上有一个索引,那么扫描的路径就有两个。一个可能是简单的顺序查找,而另一个可能就是使用索引的那个。下一步是计算出每个索引执行的开销,并且选择和返回开销最少的那个。
执行器递归地走过规划树(plan tree)并且在这个过程中检索规划所代表的记录。执行器在对关系进行扫描时使用存储系统(storage system),进行排序(sorts)和联接(joins),计算条件(qualifications)并且最终交回生成的记录。
在随后的章节里,我们将对上面的每一个步骤进行更详细的讨论,以便让我们对Postgres 的内部控制和数据结构有一个更准确的理解。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
联接是如何建立起来的
Postgres 是用一个简单的"每用户一进程"的client/server 模型实现的。在这种模式里一个客户端进程只是与一个服务器进程联接。因为我们不知道具体要建立多少个联接,所以我们不得不利用一个主进程 在每次联接请求时派生出一个新的服务器进程来。这个主进程叫做 postmaster,它监听着一个特定的 TCP/IP 端口等待进来的联接。每当检测到一个联接请求时,postmaster 进程派生出一个新的叫 postgres 的服务器进程。服务器任务(postgres 进程)相互之间使用信号灯和共享内存进行通讯,以确保在并行的数据访问过程中的数据完整性。图 \ref{connection} 显示了主进程 postmaster,服务器进程 postgres 和客户端应用之间的相互关系。
客户端进程要么是 psql 前端(用于交互的 SQL 查询)或者是任意用 libpg 库实现的用户应用。请注意用 ecpg(Postgres 用于 C 的嵌入 SQL 预处理器)实现的应用同样也使用这个库。
一旦建立起来一个联接,客户端进程就可以向后端服务器进程发送查询了。查询是通过纯文本传输的,也就是说在前端(客户端)不做任何分析处理。服务器分析查询,创建执行规划,执行该规划并且通过已经建立起来的联接把检索出来的记录返回给客户端。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
分析器阶段
分析器阶段( parser stage )包含两个部分:
在 gram.y 和 scan.l 里定义的分析器是使用 Unix 工具 yacc 和 lex 制作的。
转换处理对分析器返回的数据结构进行修改和增补。
分析器
分析器必须检查(以纯 ASCII 文本方式到来的)查询字串的语法。如果语法正确,则创建一个分析树并将之传回,否则,返回一个错误。在这个实现里我们使用了著名的 Unix 工具 lex 和 yacc。
lexer (词法)在文件 scan.l 里定义,负责识别标识符,SQL 关键字等。对于发现的每个关键字或者标识符都会生成一个记号并且传递给分析器。
分析器在文件 gram.y 里定义并且包含一套语法规则和触发规则时执行的动作。动作代码(实际上是 C 代码)用语建立分析树。
文件 scan.l 用 lex 转换成 C 源文件而 gram.y 用 yacc 转换成 gram.c。在完成这些转换后,一个通用的 C 编译器可以用于创建分析器。千万不要对生成的 C 源文件做修改,因为下一次调用 lex 或 yacc 会把它们覆盖。
注意:上面提到的转换和编译是使用跟随Postgres 发布的 makefiles 自动完成的。
对 yacc 或者 gram.y 里的语法规则的详细描述超出本文的范围。有很多关于lex 和 yacc 的书籍和文档。你在开始研究 gram.y 里给出的语法之前,你应该对 yacc 很熟悉,否则你是看不懂那里面的内容,理解不了发生了什么事情的。
为了更好地理解处理一个查询时 Postgres 里使用的数据结构,我们用一个简单的例子演示在每个阶段数据结构所做的改变。
例 54-1. 一个简单的选择(Select)
这个例子包含下面的简单的查询,这个例子将会在本章剩余各节的所有描述和图示里出现。该查询假设在Supplier Database 数据库里的表已经定义了。
select s.sname, se.pno
from supplier s, sells se
where s.sno > 2 and s.sno = se.sno;
图 \ref{parsetree} 显示了 gram
Tags:PostgreSQL,手册,开发者,手册,PostgreSQL,内部,概貌

