欢迎光临
我们一直在努力

PostgreSQL 的 SQL 执行过程详解

PostgreSQL 的 SQL 执行过程详解

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

PostgreSQL 的 SQL 执行 是一个结构清晰、模块化的流水线过程,从客户端提交 SQL 到最终返回结果,依次经历 词法分析 → 语法分析 → 语义分析 → 重写 → 规划 → 执行 等阶段。前两步依赖开源工具 Flex 和 Bison,后续各阶段由 PostgreSQL 内部模块协同完成。

pgsql 编译依赖

PostgreSQL 的词法分析和语法分析主要依赖 Flex​ 和 Bison​ 这两个经典的开源工具。

1. 核心依赖工具

# 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 解析工具:

  • Github 项目地址:https://github.com/antlr/grammars-v4/tree/master/sql/postgresql

sql 执行过程

📌 PostgreSQL SQL 执行过程详解(结合流程图与工具链)

PostgreSQL 的 SQL 执行过程是一个结构清晰、层层递进的流水线,从客户端发送 SQL 开始,经过词法分析 → 语法分析 → 语义分析 → 重写 → 规划 → 执行,最终返回结果。整个过程由多个模块协同完成,其中前两步依赖 开源工具 Flex 和 Bison。

🔄 执行流程总览(按流程图顺序)

#mermaid-svg-oBNKvjKHQVJikBxd{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oBNKvjKHQVJikBxd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oBNKvjKHQVJikBxd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oBNKvjKHQVJikBxd .error-icon{fill:#552222;}#mermaid-svg-oBNKvjKHQVJikBxd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oBNKvjKHQVJikBxd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oBNKvjKHQVJikBxd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oBNKvjKHQVJikBxd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oBNKvjKHQVJikBxd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oBNKvjKHQVJikBxd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oBNKvjKHQVJikBxd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oBNKvjKHQVJikBxd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oBNKvjKHQVJikBxd .marker.cross{stroke:#333333;}#mermaid-svg-oBNKvjKHQVJikBxd svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oBNKvjKHQVJikBxd p{margin:0;}#mermaid-svg-oBNKvjKHQVJikBxd .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-oBNKvjKHQVJikBxd .cluster-label text{fill:#333;}#mermaid-svg-oBNKvjKHQVJikBxd .cluster-label span{color:#333;}#mermaid-svg-oBNKvjKHQVJikBxd .cluster-label span p{background-color:transparent;}#mermaid-svg-oBNKvjKHQVJikBxd .label text,#mermaid-svg-oBNKvjKHQVJikBxd span{fill:#333;color:#333;}#mermaid-svg-oBNKvjKHQVJikBxd .node rect,#mermaid-svg-oBNKvjKHQVJikBxd .node circle,#mermaid-svg-oBNKvjKHQVJikBxd .node ellipse,#mermaid-svg-oBNKvjKHQVJikBxd .node polygon,#mermaid-svg-oBNKvjKHQVJikBxd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oBNKvjKHQVJikBxd .rough-node .label text,#mermaid-svg-oBNKvjKHQVJikBxd .node .label text,#mermaid-svg-oBNKvjKHQVJikBxd .image-shape .label,#mermaid-svg-oBNKvjKHQVJikBxd .icon-shape .label{text-anchor:middle;}#mermaid-svg-oBNKvjKHQVJikBxd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oBNKvjKHQVJikBxd .rough-node .label,#mermaid-svg-oBNKvjKHQVJikBxd .node .label,#mermaid-svg-oBNKvjKHQVJikBxd .image-shape .label,#mermaid-svg-oBNKvjKHQVJikBxd .icon-shape .label{text-align:center;}#mermaid-svg-oBNKvjKHQVJikBxd .node.clickable{cursor:pointer;}#mermaid-svg-oBNKvjKHQVJikBxd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oBNKvjKHQVJikBxd .arrowheadPath{fill:#333333;}#mermaid-svg-oBNKvjKHQVJikBxd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oBNKvjKHQVJikBxd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oBNKvjKHQVJikBxd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oBNKvjKHQVJikBxd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oBNKvjKHQVJikBxd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oBNKvjKHQVJikBxd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oBNKvjKHQVJikBxd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oBNKvjKHQVJikBxd .cluster text{fill:#333;}#mermaid-svg-oBNKvjKHQVJikBxd .cluster span{color:#333;}#mermaid-svg-oBNKvjKHQVJikBxd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-oBNKvjKHQVJikBxd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oBNKvjKHQVJikBxd rect.text{fill:none;stroke-width:0;}#mermaid-svg-oBNKvjKHQVJikBxd .icon-shape,#mermaid-svg-oBNKvjKHQVJikBxd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oBNKvjKHQVJikBxd .icon-shape p,#mermaid-svg-oBNKvjKHQVJikBxd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oBNKvjKHQVJikBxd .icon-shape rect,#mermaid-svg-oBNKvjKHQVJikBxd .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oBNKvjKHQVJikBxd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oBNKvjKHQVJikBxd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oBNKvjKHQVJikBxd :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

发送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 控制策略,优化执行计划的复用与重生成。


📊 总结图表(简化版流程)

┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ 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 工具,后续各阶段协同完成语义分析、逻辑重写、物理优化与数据执行,并通过执行计划缓存机制平衡性能与准确性。

赞(0)
未经允许不得转载:171主机测评 » PostgreSQL 的 SQL 执行过程详解
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址