标识查询处理组件
执行查询有 4 个单独的阶段。 按照执行顺序,这些阶段包括:
- 分析
- 转换(重写工具)
- 规划
- 执行
分析程序
分析程序负责检查查询字符串是否具有有效的语法。 分析程序由两个主要部分组成:
- gram.y:由一组语法规则和相应的操作组成。
- scan.1:识别标识符和 SQL 关键字的 lexer。 每个关键字或标识符都会触发创建一个令牌然后移交给分析程序。
分析程序会生成一个查询树,将查询分隔为可识别的部分,以了解涉及哪些表、应用了哪些筛选器等。查询树的各个部分包括:
- 命令类型 - SELECT、INSERT、UPDATE 或 DELETE。
- 范围表项 (RTE) - 关系列表,
ie
即表、子查询、联接结果等。在 SELECT 语句中,这些项显示在 FROM 关键字后面。 - 结果关系 - INSERT、UPDATE 和 DELETE 命令的结果关系,是更改生效的表或视图。
- 目标列表 - 查询的结果,在关键字 SELECT 和 FROM 之间标识。 DELETE 命令不会生成结果,因此规划器会添加一个特殊项,以允许执行程序查找要删除的行。 INSERT 命令标识应进入结果关系的新行。 对于 UPDATE 命令,目标列表描述应替换旧行的新行。
- 限定 - 一个布尔值,该值指定是否应执行最终结果行的操作。 它对应于 SQL 语句的 WHERE 子句。
- 联接树 - 这可能是 FROM 项的列表。 联接可以按任意顺序完成,也可以按特定顺序完成,例如外部联接。
- 其他 - 在此阶段不相关的项,例如 ORDER BY 子句。
重写工具
分析程序的输出将传递到“转换或重写工具”进程,除非发现错误,在这种情况下会返回错误消息。
查询重写工具通过向查询文本应用规则来重写查询文本。 重写工具将规则考虑在内,然后将修改后的查询传递给查询规划器。 行级别安全性在此阶段实现。
例如,SELECT 上的规则始终作为最后一步应用,包括 INSERT、UPDATE 和 DELETE 查询。 规则还意味着 UPDATE 查询不会覆盖现有行,而是插入新行,隐藏旧行。 提交事务后,vacuum 进程可以删除隐藏的行。
Planner
规划器的工作是获取查询规则,并了解可以执行查询的不同方式中哪种方式最快。
规划器会创建一个计划树,其中节点表示对数据的物理操作。
PostgreSQL 使用基于成本的查询优化器来查找查询的最佳计划。 规划器会评估各种执行计划,并估计所需的资源量,例如 CPU 周期、I/O 操作等。然后,此估算值转换为单位,称为“计划成本”。 选择了成本最低的计划。
但是,随着联接数的增加,可能的计划数量也呈指数级增长。 即使相对简单的查询,评估每个可能的计划也变得不可能。 可使用试探法和算法来限制可能的计划数量。 结果是所选计划可能不是最佳计划。 但是,它是可在合理时间内选出的近乎最佳的选择。
成本是规划器的最佳估计值。 成本估算的目的是在相同条件下,比较同一查询的不同执行计划。 规划器使用在表和行上收集的统计信息,生成查询的成本估算值。 若要准确估算成本,统计信息必须是最新的。
最新统计信息
查询优化器的规划器组件使用有关表和行的统计信息来生成准确的成本估算。
ANALYZE 收集有关数据库表的统计信息并将结果存储在 pg_statistic 系统目录中。 如果出现以下情况,需要运行 ANALYZE:
- 已禁用 autovacuum(通常会自动分析表)
- 已禁用 autovacuum,并且最近没有运行 ANALYZE
- 上述任一语句,并且有许多 INSERTS、UPDATES 或 DELETE 语句。
成本估算依赖于最新的统计信息,如果统计信息过时,则选择的可能是效率低下的计划。 如果未将任何参数传递给 ANALYZE,会检查数据库中的每一个表。
ANALYZE 的语法为:
ANALYZE [ VERBOSE ] [ ***table*** [ ( ***column*** [, ...] ) ] ]
VERBOSE 显示进度消息,以显示正在分析的表以及一些统计信息。
安排 VACUUM 和 ANALYZE 在使用率较低的时间每天运行。 ANALYZE 可以与其他活动并行运行,因为其只需要目标表上的读取锁。
执行者
此阶段采用规划器创建的计划,并递归处理它以提取所需的行集。 每次调用计划节点时,执行程序都必须提供一行,或者报告说它已完成。
执行程序评估所有四种 SQL 查询类型:
- SELECT
- INSERT
- UPDATE
- DELETE
对于 SELECT,执行程序将每一行作为结果集返回给客户端。
对于 INSERT,返回的每一行都会插入到指定的表中。 此任务在名为 ModifyTable 的特殊顶级计划节点中完成。
对于 UPDATE,每个计算行都包括所有更新的列值,以及目标行的行 ID。 数据将发送到 ModifyTable 节点,该节点将创建更新的行,并将旧行标记为已删除。
对于 DELETE,计划返回的唯一列是行 ID。 ModifyTable 节点使用行 ID 将行标记为已删除。