深度解析JQuick-SQL 逻辑计划到物理计划生成机制|分布式SQL原理
Meta 描述:本文基于jquick-sql架构原理解析JQuickPhysicalPlanGenerator如何把逻辑计划映射为可执行物理节点,涵盖Table Scan、Hash Join、Nested Loop Join、Hash Aggregate、Exchange与TopN,并说明Java分布式SQL引擎架构、SQL查询优化原理及gRPC分布式数据交换的衔接。
标签:jquick-sql架构原理、Physical Plan、JQuickPhysicalPlanGenerator、Hash Join、Hash Aggregate、gRPC分布式数据交换
官网示例:
- FORMULAS 示例页:https://www.jquick.org/jquick-sql
- github 地址:https://github.com/paohaijiao/jquick-sql
前言
逻辑计划回答"查询由哪些关系操作组成",物理计划(Physical Plan)进一步回答"用哪些执行节点完成这些操作"。JQuick-SQL 中的 JQuickPhysicalPlanGenerator 负责这一映射;它接收 JQuickLogicalPlanNode,生成对应的 JQuickPhysicalPlanNode,然后调用物理优化器做进一步调整。
这一步是从 SQL查询优化原理走向执行实现的分界:逻辑层的 Join 不指定算法,物理层才选择 Hash Join 或 Nested Loop Join;逻辑层的 GroupBy 不指定交换方式,物理优化后才可能出现 Exchange 与两阶段聚合。
Generator入口与节点映射
生成入口为:
java
public JQuickPhysicalPlanNode generate(JQuickLogicalPlanNode logicalPlan) {
logicalPlan.accept(this);
JQuickPhysicalPlanNode physicalPlan = logicalToPhysical.get(logicalPlan);
if (physicalPlan != null) {
physicalPlan = optimizer.optimize(physicalPlan);
}
return physicalPlan;
}
该实现先通过 Visitor 访问逻辑节点,再从映射表取得物理节点,最后执行 JQuickPhysicalPlanOptimizer。已确认的映射包括:TableScan → JQuickTableScanPhysicalNode、Filter → JQuickFilterPhysicalNode、Project → JQuickProjectPhysicalNode、Sort → JQuickSortPhysicalNode、Limit → JQuickLimitPhysicalNode,以及 Values、Empty、Window、Set Operation、Recursive Union 等节点。
Join:算法选择不写死在SQL里
处理 JQuickJoinNode 时,生成器会检查连接类型与连接条件。CROSS JOIN 或满足等值连接条件的 Join 可以构造成 JQuickHashJoinPhysicalNode;其构造参数包含 join type、左右 child、条件、join keys、build side、分布方式及别名。初始分布方式被设为 SHUFFLE_HASH,后续物理优化器会根据统计信息调整。
其他条件走 JQuickNestedLoopJoinPhysicalNode。因此,不应把"所有 JOIN 都是 Hash Join"当成项目事实:是否具备可提取的等值键会影响物理节点选择。
聚合、限制与物理优化
逻辑 GroupBy 会被转换为 JQuickHashAggregatePhysicalNode,初始阶段为 AggregateStage.SINGLE。之后物理优化器可能把大数据量且带 group keys 的聚合改写为 PARTIAL、SHUFFLE Exchange、FINAL 三段结构。
Limit 也不必保持原形。物理优化器发现 JQuickLimitPhysicalNode 的子节点为 JQuickSortPhysicalNode 时,会产生 JQuickTopNPhysicalNode。这表明 Generator 负责忠实映射逻辑语义,而优化器负责组合节点层面的执行改写。
Exchange与计划边界
JQuickExchangePhysicalNode 属于真实物理节点,保存 exchange type、partition strategy、partition keys、target parallelism 与 child。它继承 child 的输出 schema 与统计信息,本身不重新定义列结构。Exchange 为 Fragment 之间的数据再分布提供计划表示,是 Java分布式SQL引擎架构中连接本地算子和 gRPC 数据传输的关键桥梁。
text
Logical Join / GroupBy
↓
JQuickPhysicalPlanGenerator
↓
Physical Nodes
↓
JQuickPhysicalPlanOptimizer
↓
Hash Join分布调整 / TopN / Exchange / 两阶段聚合
实操代码案例
README 的嵌入式模式可以触发完整执行管线:
java
JQuickSQL sql = JQuickSQL.embedded();
try {
sql.registerTable("users", columns, rows);
JQuickDataSet result = sql.execute(
"SELECT id, name, age FROM users WHERE age >= 25 ORDER BY age DESC"
);
result.printTable();
} finally {
sql.shutdown();
}
该示例不直接暴露物理节点,但在内部链路中,扫描、过滤、投影、排序与执行均需经过计划生成。文档中的 embedded() 与 shutdown() 均为 README 已提供的真实接口。
常见问题
Physical Plan是否等于最终分布式任务?
不完全等于。物理计划之后还会经过 Fragmenter 切分,形成可调度的 Fragment。
为什么生成器后还要优化?
生成器完成逻辑到物理的基础映射;物理优化器再进行 Join 分布选择、两阶段聚合、TopN 与 Exchange 等改写。
Exchange会改变结果字段吗?
JQuickExchangePhysicalNode 的输出 schema 直接继承 child,重点是数据分发而不是列转换。
总结与拓展
JQuickPhysicalPlanGenerator 把逻辑语义落到实际执行节点,并把节点树交给物理优化器。其价值在于将 Scan、Filter、Project、Join、Aggregate、Sort、Limit 等操作落实为可执行结构,同时为 Exchange 与 Fragment 分布式调度留下明确接口。
Core Advantage
It separates logical semantics from executable algorithms and distribution decisions.
Applicable Scenarios
Physical-plan debugging, execution tuning, distributed planning, and operator development.