3. 跟踪一次简单查询的执行流程

跟踪一次简单查询的执行流程 学习卡片

任务说明

本案例以PostgreSQL 16.x源码为基础,跟踪一条最简单的查询SELECT 1;从客户端发送到服务端返回结果的完整执行链路。目标是在源码层面定位这条查询所经过的核心函数,理解查询执行的主干流程,并绘制出对应的调用流程图。案例不涉及优化器复杂路径、并行执行或索引扫描等扩展内容,聚焦于最基础的执行主线。

核心执行链路定位

PostgreSQL采用经典的进程模型:每个客户端连接对应一个后端进程(backend process)。查询从客户端到达后,依次经过词法/语法解析、分析、重写、计划、执行、结果返回几个阶段。对于SELECT 1;这类常量查询,优化器会生成一个Result节点,执行器直接计算并返回结果,不涉及表扫描。

关键函数入口位于src/backend/tcop/postgres.c中的PostgresMain(),这是后端进程的主循环。核心调用链如下:

  1. PostgresMain() → exec_simple_query():处理单条简单查询;
  2. exec_simple_query() → pg_parse_query():调用词法/语法解析器,生成原始解析树;
  3. → pg_analyze_and_rewrite():对解析树做语义分析(生成查询树Query)并应用规则重写;
  4. → pg_plan_queries():调用查询优化器,生成执行计划(PlannedStmt);
  5. → PortalRun():进入执行阶段,通过执行器运行计划;
  6. → PortalRunSelect() → ExecutorRun():执行器开始运行;
  7. → standard_ExecutorRun() → ExecutePlan():驱动执行节点树;
  8. → ExecResult():执行Result节点,计算常量表达式并返回一行结果;
  9. → 结果通过SendQueryResults()返回客户端。

关键源码位置与函数职责

函数文件位置职责
PostgresMain()src/backend/tcop/postgres.c后端进程主循环,接收客户端消息并分发
exec_simple_query()src/backend/tcop/postgres.c处理无绑定参数的简单查询
pg_parse_query()src/backend/tcop/postgres.c调用解析器生成RawStmt列表
pg_analyze_and_rewrite()src/backend/tcop/postgres.c语义分析+规则重写,生成Query
pg_plan_queries()src/backend/tcop/postgres.c生成PlannedStmt执行计划
PortalRun()src/backend/tcop/pquery.c运行Portal,进入执行阶段
ExecutorRun()src/backend/executor/execMain.c执行器统一入口
ExecutePlan()src/backend/executor/execMain.c驱动节点树,逐行获取结果
ExecResult()src/backend/executor/nodeResult.c执行Result节点,计算目标列

验证方法

要验证上述调用链,推荐使用gdb附加到后端进程并设置断点。操作步骤如下:

  1. 编译PostgreSQL时保留调试符号(./configure --enable-debug);
  2. 启动数据库后,找到后端进程PID:SELECT pg_backend_pid();;
  3. 使用gdb附加:gdb -p PID;
  4. 在关键函数设置断点:break exec_simple_query、break pg_parse_query、break pg_analyze_and_rewrite、break pg_plan_queries、break PortalRun、break ExecResult;
  5. 在另一个终端执行psql -c "SELECT 1;";
  6. 观察gdb中断点命中的顺序,应依次经过上述函数。

判定标准:若断点按exec_simple_query → pg_parse_query → pg_analyze_and_rewrite → pg_plan_queries → PortalRun → ExecResult的顺序依次命中,且最终客户端收到结果1,则验证通过。

执行流程时序图

SELECT 1; 执行流程核心调用链 客户端 psql PostgreSQL 后端进程 发送查询 SQL PostgresMain() exec_simple_query() pg_parse_query() 词法/语法解析 pg_analyze_and_rewrite() 语义分析+重写 pg_plan_queries() 生成计划 PortalRun() ExecResult() 计算常量 1 返回结果行 收到结果

补充说明

上述链路中,PortalRun()内部还会经过PortalRunSelect()、ExecutorRun()、standard_ExecutorRun()、ExecutePlan()等函数,最终才到达ExecResult()。这些中间函数位于src/backend/tcop/pquery.c和src/backend/executor/execMain.c中,属于执行器的标准调用路径。对于SELECT 1;,优化器生成的计划树只有一个Result节点,因此执行器直接调用ExecResult()完成计算,无需访问存储引擎。

若希望观察更详细的函数调用层次,可在gdb中使用bt命令查看完整调用栈,或在ExecutorRun()处设置断点后单步跟踪。本案例给出的链路已覆盖从客户端到执行节点的完整主干,足以支撑对PostgreSQL查询执行流程的源码级理解。