为什么分布式查询引擎,最后都在 Execution Layer“还债”

16 阅读4分钟

写分布式查询引擎久了,你会发现一个规律:

不管一开始你多关注 SQL 解析、多欣赏优化器、多精心设计存储层,
系统规模一上来,复杂度一定会集中爆发在 Execution Layer。

而且不是“慢慢变复杂”,而是:

  • 开始返工
  • 开始推翻
  • 开始重写
  • 开始怀疑最初的架构判断

这不是某个团队的问题,而是分布式查询引擎的工程宿命

这篇文章,我想结合最近一段时间在 Execution Layer、Rust 实现、Shuffle、返工设计上的一些总结,聊清楚三件事:

  1. 为什么复杂度一定会压到 Execution Layer
  2. 为什么很多设计一开始看不出问题,后面却必返工
  3. 工程上真正值得提前“防雷”的地方在哪里

一、Execution Layer 承担的,从来不是“执行”这么简单

很多人对查询引擎的模块划分是这样的:

  • SQL 解析
  • 查询优化
  • 执行计划
  • 执行层
  • 存储

但真正写过系统的人会发现,这个划分极具迷惑性

原因在于:

  • 前面的模块解决的是 静态问题
  • Execution Layer 面对的是 动态、不确定、会失败的现实世界

具体来说:

  • 数据分布是否均匀?——跑起来才知道
  • 节点会不会慢?——运行时才发生
  • 网络会不会抖?——随时可能
  • 内存会不会爆?——压力上来才暴露

所有“理想假设失效”的地方,最终都会落在 Execution Layer。

这也是为什么:

  • 优化器可以几年一大改
  • Execution Layer 却几乎每个版本都在重构

二、Execution Layer 的复杂度,来自“不确定性的叠加”

Execution Layer 之所以难,不是因为单点问题难,而是因为多个问题会同时发生

尤其是在以下几个场景中。


1️⃣ Shuffle:不是一个阶段,而是系统性成本放大器

从表面看,Shuffle 只是一次数据重分布:

  • GROUP BY
  • JOIN
  • ORDER BY

但在 Execution Layer 里,Shuffle 往往意味着:

  • 网络带宽被拉满
  • 内存峰值不可控
  • 磁盘 I/O 被迫介入
  • CPU 同时承压

更危险的是:

Shuffle 会放大系统中的所有不确定性。

  • 一个慢节点 → 拖垮全局
  • 一次数据倾斜 → OOM
  • 一次网络抖动 → 查询雪崩

很多系统的稳定性问题,最终都会追溯到 Shuffle。


2️⃣ 并发与调度:越“聪明”,越容易失控

在 Execution Layer,很多返工都源于一个误判:

“并发越高,性能越好。”

现实往往相反:

  • CPU-bound 算子 async 化 → 调度成本暴涨
  • 多线程 + async 混用 → 执行路径不可推理
  • 并发度过高 → 资源争抢 + 尾延迟放大

在 Execution Layer,并发策略一旦选错,几乎只能推翻重来


3️⃣ 内存与生命周期:Rust 并不会“自动帮你省内存”

Rust 的内存安全很强,但在 Execution Layer 里,常见问题并不是“内存泄漏”,而是:

  • 中间结果被意外持有
  • 生命周期被无意识拉长
  • 内存峰值持续抬高

尤其在 Shuffle、Join、聚合场景下,一次设计失误,就可能让系统在大数据量下直接崩溃。


三、为什么 Execution Layer 的设计,最容易返工

综合来看,Execution Layer 返工,往往不是因为“代码写错了”,而是早期设计过于将就

几个高频返工点,几乎是共性的:


❌ 执行模型不清晰

  • 把查询当 SQL 字符串
  • 或临时拼装执行逻辑

后期再补执行计划、算子抽象、调度模型,在 Rust 里几乎必然重构。


❌ 过度共享状态

  • Arc<Mutex<T>> 快速实现
  • async + 锁 混用

初期能跑,后期性能和稳定性双双失控。


❌ 把 Shuffle 当“优化点”,而不是“风险点”

很多系统是在:

数据量上来之后,才意识到 Shuffle 根本不是“调一下参数就能救”的问题。


❌ 错误边界不清晰

  • task / stage / query 错误混在一起
  • panic 作为常规路径

在分布式环境下,这是事故的温床。


四、Execution Layer 工程上的“真实共识”

如果非要总结几条迟早会达成的工程共识,大概是:

  • 能不 Shuffle,就不要 Shuffle
  • 并发不是越多越好,而是越可预测越好
  • Execution Layer 的稳定性,比极致性能更重要
  • 很多优化,本质是在“止损”,不是在“提速”

Execution Layer 不是“跑算子”的地方,而是:

调度现实复杂度的地方。


结语:Execution Layer,是系统真正成熟的标志

如果你的分布式查询引擎开始在 Execution Layer 变复杂,开始返工、重构、反复推敲设计——

这通常不是坏事。

这意味着系统已经走出了“理想模型”,开始真正面对:

  • 不确定的数据分布
  • 不可靠的节点
  • 不稳定的网络
  • 不可预测的负载

Execution Layer 的复杂度,往往是一个系统进入真实世界后的必修课

后续我也会继续围绕 Execution Layer,分享更多关于 Shuffle、调度、内存、并发和稳定性的工程经验,欢迎交流。