PostgreSQL 的 SQL 执行过程详解
-
- [pgsql 编译依赖](#pgsql 编译依赖)
-
- [1. 核心依赖工具](#1. 核心依赖工具)
- [2. 第三方解析库](#2. 第三方解析库)
- [sql 执行过程](#sql 执行过程)
-
- [📌 PostgreSQL SQL 执行过程详解(结合流程图与工具链)](#📌 PostgreSQL SQL 执行过程详解(结合流程图与工具链))
-
- [🔄 执行流程总览(按流程图顺序)](#🔄 执行流程总览(按流程图顺序))
- [🧩 各阶段详细说明](#🧩 各阶段详细说明)
- [🛠️ 补充:执行过程中的缓存与优化机制(来自图中中文注释)](#🛠️ 补充:执行过程中的缓存与优化机制(来自图中中文注释))
- [📊 总结图表(简化版流程)](#📊 总结图表(简化版流程))
- [🧠 附加说明:为什么需要这么多步骤?](#🧠 附加说明:为什么需要这么多步骤?)
- [✅ 总结](#✅ 总结)
PostgreSQL 的 SQL 执行 是一个结构清晰、模块化的流水线过程,从客户端提交 SQL 到最终返回结果,依次经历 词法分析 → 语法分析 → 语义分析 → 重写 → 规划 → 执行 等阶段。前两步依赖开源工具 Flex 和 Bison,后续各阶段由 PostgreSQL 内部模块协同完成。
pgsql 编译依赖
PostgreSQL 的词法分析和语法分析主要依赖 Flex 和 Bison 这两个经典的开源工具。
1. 核心依赖工具
bash
# Flex (词法分析器)
作用:负责将 SQL 字符串拆解成一个个的"单词"(Token),比如识别 SELECT、FROM等关键字,以及表名、数字等。
对应文件:源码中的 `src/backend/parser/scan.l` 文件。
# Bison (语法分析器)
作用:根据 SQL 语法规则,将 Flex 生成的 Token 组合成语法树(Parse Tree)。
对应文件:源码中的 `src/backend/parser/gram.y` 文件。
2. 第三方解析库
如果你是在自己的项目中(如:.NET、Java、Go、Python 等)解析 PostgreSQL 的 SQL 语句,通常会使用以下第三方库,它们内部也依赖特定的解析工具:
- .NET:Npgsql(基于 libpg_query)或 SqlParser(基于Npgsql)
- Java:dt-sql-parser(基于 ANTLR4) 或 TDSQL Parser。
- Go:postgresql-parser(基于 goyacc)。
- C/C++:libpg_query(基于 PostgreSQL 官方源码)。
- Python:SQLGlot(基于 Lark)。
其他编程语言基于 ANTLR4 的 pgsql 解析工具:
sql 执行过程
📌 PostgreSQL SQL 执行过程详解(结合流程图与工具链)
PostgreSQL 的 SQL 执行过程是一个结构清晰、层层递进的流水线,从客户端发送 SQL 开始,经过词法分析 → 语法分析 → 语义分析 → 重写 → 规划 → 执行,最终返回结果 。整个过程由多个模块协同完成,其中前两步依赖 开源工具 Flex 和 Bison。
🔄 执行流程总览(按流程图顺序)
发送sql
是
否
Client
Parser
Analyzer
Utility Command?
Executor
Rewriter
Planner
Result
🧩 各阶段详细说明
1️⃣ Client(客户端)
- 作用:用户通过
psql、ADO.NET、JDBC、ODBC等客户端工具提交SQL语句(query string)。 - 输出:原始
SQL字符串,传递给PostgreSQL服务端。
2️⃣ Parser(解析器)→ 依赖 Flex + Bison
这是 SQL 执行的"入口关卡",主要负责:
-
词法分析(
Lexical Analysis):使用
Flex工具,根据scan.l文件定义的规则,将SQL字符串切分成一个个Token(如SELECT, FROM, table_name, WHERE, =, 123等)。 -
语法分析(
Syntactic Analysis):使用
Bison 工具,根据gram.y文件定义的语法规则,将Token序列组合成初步的语法树(raw parse tree)。
输出:raw parse tree ------ 一个结构化的语法树,但还未绑定数据库对象或类型信息。
✅ 关键点:Parser 只检查语法是否合法,不关心语义(如表是否存在、字段是否合法)。
3️⃣ Analyzer(分析器)
- 作用:对
Parser生成的语法树进行语义分析和数据库对象解析。
具体操作:
Add detailed info:为每个节点添加类型、列名、函数签名等详细元数据。Database lookups:查询系统目录(如pg_class, pg_attribute)确认表、列、索引等是否存在且合法。
输出:query tree------ 语义完整的查询树,可用于后续优化和执行。
✅ 关键点:Analyzer 确保 SQL 不仅是"语法正确",而且是"语义合法"。
4️⃣ Utility Command?(工具命令判断)
这是一个分支判断节点:
- 如果
SQL是Utility命令(如CREATE TABLE, DROP INDEX, COPY, ALTER, VACUUM等),则跳过Rewriter和Planner,直接交由Executor执行(因为这些命令不需要查询计划)。 - 如果是
DML查询(如SELECT, INSERT, UPDATE, DELETE),则进入下一步:Rewriter。
✅ 关键点:Utility 命令通常是"元操作",不涉及复杂查询优化。
5️⃣ Rewriter(重写器)
- 作用:根据规则对查询树进行逻辑重写,不改变语义但可能简化或优化结构。
典型重写规则:
- 视图展开(
View Expansion):将SELECT * FROM my_view替换为视图定义的底层查询。 - 规则系统(
Rule System):用户自定义的重写规则(如自动重写SELECT * FROM t WHERE id = 1为SELECT * FROM t WHERE id = 1 AND active = true)。 - 常量折叠、逻辑表达式化简等。
输出:rewritten query tree ------ 经过逻辑重写后的查询树。
✅ 关键点:Rewriter 是逻辑优化阶段,为 Planner 提供更简洁、更优化的输入。
6️⃣ Planner(规划器 / 优化器)
- 作用:从多个可能的执行方案中选择"最优"的物理执行计划。
核心任务:
- 生成多个可能的执行路径(如使用哪个索引、连接顺序、扫描方式等)。
- 估算每个路径的代价(
Cost Estimation):基于统计信息(如行数、数据分布、I/O、CPU 开销等)。 - 选择代价最低的计划(
Choose the best plan)。
输出:plan tree ------ 一棵物理执行计划树,描述如何一步步访问数据、如何连接、如何排序等。
✅ 关键点:Planner 是性能的关键,它决定了 SQL 执行的效率。
7️⃣ Executor(执行器)
- 作用:根据
Planner生成的plan tree,实际访问数据、执行操作、返回结果。
具体操作:
- 调用存储引擎、访问表、索引、执行排序、聚合、连接等。
- 使用工作进程(
Worker)并行执行(如并行查询)。 - 将中间结果缓存(如
Hash Join的哈希表)。
输出:最终结果集(Result Set)或操作成功/失败状态。
✅ 关键点:Executor 是"实干家",真正执行数据访问和计算。
🛠️ 补充:执行过程中的缓存与优化机制(来自图中中文注释)
图中底部文字提到了 执行计划缓存机制(类似 Oracle 的 bind peek 或 adaptive plan):
-
首次执行:
解析、分析、重写、规划全过程执行,生成"通用执行计划"(
generic plan)。同时记录第一次执行的"实际变量值"(如
WHERE id = :1中的:1值)。 -
后续执行:
比较"通用计划"与"首次执行计划"的优劣。
如果通用计划更优 → 以后都使用通用计划(避免重复规划,提升性能)。
如果首次计划更优 → 以后每次重新生成计划(适应变量变化,避免"绑定变量窥探"问题)。
-
效果:在"稳定变量"场景下提升性能,在"变量变化大"场景下保持准确性。
✅ 关键点:PostgreSQL 10+ 引入了 plan_cache_mode 和 plan_cache_mode 控制策略,优化执行计划的复用与重生成。
📊 总结图表(简化版流程)
bash
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Client │ ──→ │ Parser │ ──→ │ Analyzer│ ──→ │ Rewriter │ ──→ │ Planner │ ──→ │ Executor│ ──→ Result
└─────────┘ └─────────┘ └─────────┘ └─────────────┘ └─────────┘ └─────────┘ └─────────┘
↓ (raw parse tree) ↑ (query tree) ↑ (rewritten query tree) ↑ (plan tree)
│ │ │ │
└───────── Flex/Bison ──┘ └───────── 优化选择 ──────┘
🧠 附加说明:为什么需要这么多步骤?
- 解耦设计:每个阶段职责单一,便于维护、扩展和调试。
- 模块化:
Parser、Analyzer、Rewriter、Planner、Executor可独立演进。 - 性能优化:
Planner通过代价模型选择最优路径,Executor通过并行、缓存、索引等手段加速。 - 语义安全:
Analyzer确保SQL不会操作不存在的对象,避免运行时错误。
✅ 总结
PostgreSQL 的 SQL 执行过程是一个从"字符串 → 语法树 → 语义树 → 重写树 → 执行计划树 → 数据结果"的流水线,前两步依赖 Flex 和 Bison 工具,后续各阶段协同完成语义分析、逻辑重写、物理优化与数据执行,并通过执行计划缓存机制平衡性能与准确性。