PostgreSQL 的 SQL 执行过程详解

PostgreSQL 的 SQL 执行过程详解

    • [pgsql 编译依赖](#pgsql 编译依赖)
      • [1. 核心依赖工具](#1. 核心依赖工具)
      • [2. 第三方解析库](#2. 第三方解析库)
    • [sql 执行过程](#sql 执行过程)
      • [📌 PostgreSQL SQL 执行过程详解(结合流程图与工具链)](#📌 PostgreSQL SQL 执行过程详解(结合流程图与工具链))
        • [🔄 执行流程总览(按流程图顺序)](#🔄 执行流程总览(按流程图顺序))
        • [🧩 各阶段详细说明](#🧩 各阶段详细说明)
        • [🛠️ 补充:执行过程中的缓存与优化机制(来自图中中文注释)](#🛠️ 补充:执行过程中的缓存与优化机制(来自图中中文注释))
      • [📊 总结图表(简化版流程)](#📊 总结图表(简化版流程))
      • [🧠 附加说明:为什么需要这么多步骤?](#🧠 附加说明:为什么需要这么多步骤?)
      • [✅ 总结](#✅ 总结)

PostgreSQLSQL 执行 是一个结构清晰、模块化的流水线过程,从客户端提交 SQL 到最终返回结果,依次经历 词法分析 → 语法分析 → 语义分析 → 重写 → 规划 → 执行 等阶段。前两步依赖开源工具 FlexBison,后续各阶段由 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?(工具命令判断)

这是一个分支判断节点:

  • 如果 SQLUtility 命令(如 CREATE TABLE, DROP INDEX, COPY, ALTER, VACUUM 等),则跳过 RewriterPlanner,直接交由 Executor 执行(因为这些命令不需要查询计划)。
  • 如果是 DML 查询(如 SELECT, INSERT, UPDATE, DELETE),则进入下一步:Rewriter

✅ 关键点:Utility 命令通常是"元操作",不涉及复杂查询优化。


5️⃣ Rewriter(重写器)

  • 作用:根据规则对查询树进行逻辑重写,不改变语义但可能简化或优化结构。

典型重写规则:

  • 视图展开(View Expansion):将 SELECT * FROM my_view 替换为视图定义的底层查询。
  • 规则系统(Rule System):用户自定义的重写规则(如自动重写 SELECT * FROM t WHERE id = 1SELECT * 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 是"实干家",真正执行数据访问和计算。


🛠️ 补充:执行过程中的缓存与优化机制(来自图中中文注释)

图中底部文字提到了 执行计划缓存机制(类似 Oraclebind peekadaptive plan):

  • 首次执行:

    解析、分析、重写、规划全过程执行,生成"通用执行计划"(generic plan)。

    同时记录第一次执行的"实际变量值"(如 WHERE id = :1 中的 :1 值)。

  • 后续执行:

    比较"通用计划"与"首次执行计划"的优劣。

    如果通用计划更优 → 以后都使用通用计划(避免重复规划,提升性能)。

    如果首次计划更优 → 以后每次重新生成计划(适应变量变化,避免"绑定变量窥探"问题)。

  • 效果:在"稳定变量"场景下提升性能,在"变量变化大"场景下保持准确性。

✅ 关键点:PostgreSQL 10+ 引入了 plan_cache_modeplan_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 不会操作不存在的对象,避免运行时错误。

✅ 总结

PostgreSQLSQL 执行过程是一个从"字符串 → 语法树 → 语义树 → 重写树 → 执行计划树 → 数据结果"的流水线,前两步依赖 FlexBison 工具,后续各阶段协同完成语义分析、逻辑重写、物理优化与数据执行,并通过执行计划缓存机制平衡性能与准确性。

相关推荐
小鸡脚来咯2 小时前
SQL表连接
java·开发语言·数据库
Henray20242 小时前
SQL 窗口函数
大数据·数据库·sql
顶点多余2 小时前
Mysql--索引的操作
数据库·mysql
RDCJM2 小时前
Neo4j图数据库学习(二)——SpringBoot整合Neo4j
数据库·学习·neo4j
杀神lwz2 小时前
MongoDB入门+深入(三)--mongdbSql
数据库·mongodb
枫叶丹42 小时前
复杂SQL性能突围:代价驱动的连接条件下推策略与工程实践
数据库·sql·金仓数据库
聆风吟º2 小时前
直击复杂 SQL 瓶颈:金仓基于代价的连接条件下推技术落地
java·数据库·sql·kingbasees
努力进修3 小时前
复杂查询性能优化:连接条件下推的代价模型设计与实践
数据库·sql·性能优化
原来是猿3 小时前
Linux-【文件系统上】
linux·服务器·数据库