PostgreSQL 的 SQL 查询之旅

当输入 SELECT * FROM users WHERE id = 42;并执行时,这条看似简单的 SQL 语句,实际上会在 PostgreSQL 内部触发一段复杂而精密的处理流程。该过程涉及多个后台进程、精细的内存管理机制,以及数十年数据库优化研究的成果。

查询执行的五个阶段

无论查询复杂与否,在 PostgreSQL 中都会经历同一条基本路径:

解析(Parsing) → 分析(Analysis) → 重写(Rewriting) → 规划(Planning) → 执行(Execution)

SQL 文本从一端进入,查询结果从另一端返回。每一个阶段内部都包含大量细致而关键的处理逻辑。

查询的起点:SQL 发送过程

以示例查询语句为例,从提交时刻开始追踪整个执行过程。应用程序首先与 PostgreSQL 服务器建立连接,随后通过 PostgreSQL 通信协议发送查询请求。

需要重点关注的是:当发送语句 SELECT * FROM users WHERE id = 42;时,PostgreSQL 会原封不动地接收该纯文本格式的语句。无论通过 psql 终端输入、应用程序调用,还是借助 ORM 框架执行,SQL 语句最终都会以文本字符串的形式传递至服务器。

服务器在接收文本后,会进行基础校验,例如字符编码是否合法、语句格式是否完整。随后,正式进入查询处理流程。

第一阶段:解析 ------ 从文本到结构

解析器是查询处理的首个环节,核心任务是将 SQL 文本转换为结构化的解析树(Parse Tree)。

在该阶段,解析器会逐字符读取 SQL 语句,并依据 PostgreSQL 定义的 SQL 语法规则进行匹配和拆解,识别其中的关键字(如 SELECTFROMWHERE)、表名、列名、运算符等语法要素。

这一过程类似于语言学中的句法分析,只关注语法结构本身,而不涉及语义含义。

例如,SELECT name FROM users WHERE id = 42;解析完成后,系统可以明确:

  • 存在一个 SELECT 子句,包含列引用 name
  • 存在一个 FROM 子句,引用表 users
  • 存在一个 WHERE 子句,包含条件表达式 id = 42

但此时解析器并不知道 users 表是否真实存在、name 是否为有效列名,也不了解涉及字段的数据类型。这些语义层面的验证工作,将由下一阶段完成。

第二阶段:分析 ------ 语义校验与绑定

分析器在解析树的基础上,构建语义有效的查询树(Query Tree),这是从"语法正确"迈向"语义正确"的关键阶段。

该阶段主要完成以下工作:

  • 对象解析 :在系统目录中查找 users 表,校验其是否存在;确认 nameid 是否为合法列。
  • 类型检查 :校验 id = 42 是否成立,例如 id 的数据类型是否支持与整数进行比较,对应的比较运算符是否存在。
  • 权限校验 :确认当前会话是否具备访问 users 表及相关列的 SELECT 权限。
  • 语义信息补充:为查询树补充对象标识信息,如表和列对应的 OID、字段类型等。

若任一环节失败(表不存在、字段拼写错误、权限不足等),查询将在此阶段终止并返回错误。

完成该阶段后,查询的含义已被 PostgreSQL 完整理解,接下来进入自动转换处理。

第三阶段:重写 ------ 自动规则转换

重写器在语义有效的查询树基础上,应用一系列自动化转换规则,生成最终待执行的查询结构。常见的转换包括:

  • 视图展开 :当查询对象为视图时,重写器会将视图查询转换为对底层基表的查询。例如,若视图 active_users 的定义为 SELECT * FROM users WHERE status = 'active',则查询 SELECT * FROM active_users 会被重写为直接查询 users 表并附加过滤条件 status = 'active'
  • 行级安全策略(RLS) :若表定义了安全策略,重写器会自动注入额外的 WHERE 条件以实现访问控制。例如,存在按租户隔离数据的策略时,原查询 SELECT * FROM users WHERE id = 42 可能被重写为 SELECT * FROM users WHERE id = 42 AND tenant_id = 123
  • 用户自定义规则:通过规则系统定义的查询重写逻辑(在现代应用中相对较少使用)。

对于简单查询,该阶段可能不会发生明显变化;但在包含视图、安全策略的复杂系统中,重写可能对查询结构产生显著影响。

第四阶段:规划 ------ 寻找最优执行路径

规划器负责解决一个核心问题:如何以最低成本执行该查询。

这一阶段是 PostgreSQL 中最复杂、最具智能化特征的部分,涉及多维度决策。

访问路径选择

对于查询中涉及的每张表,规划器需要决定数据的读取方式。例如,是对 users 表执行全表顺序扫描,还是利用 id 字段上的索引直接定位目标数据行。规划器需要决定采用何种方式访问数据:

  • 顺序扫描(Sequential Scan)
  • 索引扫描(Index Scan / Bitmap Scan)

规划器会综合评估表规模、索引可用性及选择性。有时,即便存在索引,顺序扫描也可能更高效。

连接策略选择

在多表查询中,规划器需同时确定:

  • 表的连接顺序
  • 每一步连接所采用的算法

连接顺序的影响至关重要。例如,先连接表 A 和表 B,再与表 C 连接,和先连接表 B 和表 C,再与表 A 连接,两种方式的执行效率可能存在巨大差异。规划器会评估多种连接顺序,筛选出最优方案。

针对每个连接操作,PostgreSQL 支持的主要连接算法包括:

  • 嵌套循环连接(Nested Loop):适用于小数据集,或当连接操作的其中一方数据量极少的场景。
  • 哈希连接(Hash Join):在内存充足的情况下,对中大型数据集的连接操作具有较高效率。
  • 归并连接(Merge Join):适用于两个输入数据集均已排序的场景。

不同连接顺序和算法组合,对整体性能影响巨大,规划器会评估多种可能性。

统计信息的作用

所有规划决策高度依赖统计信息。PostgreSQL 通过 ANALYZE 收集并维护表统计数据,包括:

  • 表的总行数
  • 各字段的不同值数量
  • 数据分布情况

这些信息用于估算过滤条件和连接操作的结果规模,是成本评估的基础。统计信息不准确将直接导致规划决策偏差。

成本估算与最终计划

规划器会对多种候选执行计划进行成本估算,综合考虑:

  • 磁盘 I/O 成本(从磁盘或缓存中读取数据页)
  • CPU 计算成本(数据行处理、条件表达式计算)
  • 内存消耗(排序、哈希操作)

最终选择成本最低的方案作为执行计划。当查询涉及大量表连接时,PostgreSQL 会启用遗传查询优化器(Genetic Query Optimizer)以避免组合爆炸。

规划结果可通过 EXPLAIN 查看,例如:

复制代码
EXPLAIN SELECT name FROM users WHERE id = 42;

第五阶段:执行 ------ 生成结果

执行器依据执行计划逐步获取数据,并向客户端返回结果。PostgreSQL 采用拉取式(Pull-based)执行模型。

在该模型中,上层节点按需向下层节点请求数据,而非由下层主动推送。这种机制具备良好的内存效率,并天然支持 LIMIT 等提前终止操作。

仍以查询 SELECT name FROM users WHERE id = 42 为例,其执行计划的输出结果可能如下:

复制代码
                        QUERY PLAN
-----------------------------------------------------------
 Index Scan using users_id_idx on users  (cost=0.00..8.27 rows=1 width=64)
   Filter: (id = 42)
(2 rows)

以示例查询为例,执行流程为:

  1. 执行计划的顶层节点(负责向客户端返回结果)发起数据行请求。
  2. 该请求触发索引扫描节点,向其下层节点发起数据请求。
  3. 索引扫描节点利用 users_id_idx 索引定位满足条件 id = 42 的数据行。
  4. 从磁盘或缓存中读取目标数据行,并应用过滤条件进行校验。
  5. 数据结果沿执行计划逆向传递:索引扫描节点 → 客户端。

拉取式模型具有显著的内存效率优势,因为 PostgreSQL 仅在下游节点需要数据时才执行处理操作。同时,该模型也简化了结果集限制与提前终止等功能的实现 ------ 只需停止向上游节点请求数据即可。

执行器逐行处理数据,按照通信协议的要求格式化结果,然后通过网络连接将数据发送至客户端。

所有结果发送完成后,PostgreSQL 会自动执行清理操作:

  • 销毁临时内存上下文
  • 释放执行过程中获取的各类锁资源
  • 删除执行期间生成的临时文件
    至此,当前后端处理进程恢复空闲状态,准备接收下一个查询请求。

全流程回顾

SELECT name FROM users WHERE id = 42 为例,完整流程如下:

  1. 查询发送:应用程序建立连接,以纯文本形式发送查询语句。
  2. 解析:将文本转换为解析树,完成语法结构校验。
  3. 分析:验证语义合法性,补充元数据信息,生成查询树。
  4. 重写:执行自动转换操作,如视图展开、安全策略应用。
  5. 规划:评估访问路径、连接策略,结合统计信息生成最优执行计划。
  6. 执行:基于拉取式模型执行计划,生成并返回查询结果。
  7. 清理:释放资源,恢复进程空闲状态。

所有查询均遵循该路径,区别仅在于各阶段的复杂程度。

核心价值

理解 PostgreSQL 查询执行流程,能够带来以下核心价值:

  1. 编写更高效的查询语句:掌握规划器的工作原理,可针对性地优化查询结构,例如理解部分查询无法使用索引的原因、连接顺序对性能的影响,以及公用表表达式(CTE)与子查询的适用场景差异。
  2. 精准诊断性能问题:当查询执行缓慢时,可通过 EXPLAIN 命令定位问题环节,判断是规划器选择了低效执行路径、统计信息过期,还是缺少必要的索引。
  3. 设计更合理的数据库架构:基于查询执行机制的理解,能够优化索引设计、表分区方案以及视图的使用方式。
  4. 认知数据库底层复杂度:一条简单的 SELECT 语句背后,隐藏着连接管理、内存分配、语法校验、语义分析、规则应用、成本优化以及拉取式执行等一系列复杂机制,而这一切都在后台透明运行。

原文链接:

https://internals-for-interns.com/posts/sql-query-roadtrip-in-postgres/

作者:Jesús Espino

相关推荐
云和数据.ChenGuang15 小时前
人工智能实践之基于CNN的街区餐饮图片识别案例实践
人工智能·深度学习·神经网络·机器学习·cnn
刘懂Dawn15 小时前
07844_人工智能导论_复习资料
人工智能
北京耐用通信15 小时前
耐达讯自动化赋能:Canopen转Profibus网关水泵连接新范式
人工智能·科技·物联网·自动化·信息与通信
musenh15 小时前
redis和jedis
数据库·redis·缓存
莳花微语15 小时前
磐维数据库的权限使用
数据库
小途软件15 小时前
ssm327校园二手交易平台的设计与实现+vue
java·人工智能·pytorch·python·深度学习·语言模型
小咖自动剪辑15 小时前
小咖批量剪辑助手:Windows 视频批量自动剪辑与混剪工具
人工智能·音视频·语音识别·实时音视频·视频编解码
京东零售技术15 小时前
ACM Multimedia | 京东零售广告创意:统一的布局生成和评估模型
人工智能
阿东在coding15 小时前
MCP Skill Subagent 实战指南
人工智能