Apache Doris内存不够,为什么Tablet不排队一个个处理,而是报错,请问这个解释对吗?

Viewed 6

要理解 Apache Doris 内存不足时不采用 Tablet 排队处理,而是直接报错的原因,需要从其设计目标、MPP 架构特性、内存模型、资源调度逻辑四个核心维度拆解 ——Doris 的本质是 “低延迟实时 OLAP 数据库”,而非 “批处理任务调度系统”,其设计优先级决定了 “快速失败” 比 “排队等待” 更合理。

一、核心前提:Doris 的设计目标与 Spark 的本质差异

首先要明确一个关键区别:

  • Spark是 “批处理 / 流处理引擎”,核心目标是 “稳定处理大规模数据”,允许任务排队、资源动态抢占(比如 YARN 队列调度),延迟预期通常是 “分钟级”;
  • Doris是 “MPP 架构的实时 OLAP 数据库”,核心目标是 “秒级 / 亚秒级响应复杂分析查询”,延迟预期是 “交互式”,不允许因排队导致查询响应时间不可控(比如用户等 10 分钟才出结果,违背 OLAP 场景需求)。

这种设计目标的差异,直接决定了两者面对 “资源不足” 时的处理逻辑:Doris 优先保证 “查询延迟可控”,Spark 优先保证 “任务最终能执行”。

二、关键原因 1:MPP 架构的 “并行性依赖”,排队会破坏查询整体效率

Doris 的查询执行基于MPP(大规模并行处理)架构,其核心逻辑是:

  1. 一个查询会根据表的 “分区 + 分桶” 拆分为多个子任务(每个子任务对应 1 个或多个 Tablet,Tablet 是 Doris 最小的存储 / 计算单元);

  2. 这些子任务会被分配到多个 BE 节点并行执行(每个 BE 处理本地存储的 Tablet);

  3. 最终结果需要等待所有子任务完成后汇总(比如聚合查询需要所有 BE 的局部结果相加,Join 需要所有 BE 的关联结果合并)。

如果某个 Tablet 因内存不足而 “排队等待”,会导致:

  • 整个查询变成 “串行等待”:即使其他 BE 的子任务已经完成,也必须等排队的 Tablet 处理完才能汇总结果,查询延迟会从 “秒级” 飙升到 “分钟级”,完全违背 OLAP 的交互式需求;
  • 资源浪费:已完成子任务的 BE 节点释放了内存,但排队的 Tablet 可能还在等其他查询释放资源,导致集群资源利用率碎片化,反而降低整体吞吐量。

三、关键原因 2:Doris 的 “本地内存优先” 模型,排队会导致内存管理失控

Doris 的计算逻辑高度依赖BE 节点的本地内存,其内存使用有两个核心特点:

  1. 计算与存储绑定:Tablet 的计算在存储它的 BE 节点上本地执行(避免跨节点数据传输),计算过程中大量操作(如 Hash Join、Sort、聚合)依赖 “内存驻留”(即使有磁盘溢写,也是 “内存不足时的兜底方案”,而非默认逻辑);
  2. 内存预分配与硬限制:Doris 通过mem_limit(查询内存上限)、exec_mem_limit(单算子内存上限)等参数,提前为每个查询分配内存配额 —— 设计上假设 “查询在启动前已评估好所需内存”,如果实际执行中某个 Tablet 的内存超过配额,说明 “查询本身未优化” 或 “资源配置不合理”,而非 “临时资源不足”。

如果允许 Tablet 排队,会导致 BE 节点的内存管理逻辑复杂度剧增:

  • 需维护 “Tablet 排队队列”,跟踪每个 Tablet 的内存需求、等待状态,可能引发死锁(比如 A 查询的 Tablet 等 B 查询释放内存,B 查询的 Tablet 又等 A 释放);
  • 内存碎片问题:排队过程中,释放的内存可能无法被后续 Tablet 高效利用(比如释放 1GB 内存,但排队的 Tablet 需要 2GB),反而加剧内存浪费;
  • 无法预判等待时间:排队的 Tablet 不知道要等多久(可能其他查询一直占用内存),导致查询响应时间完全不可控,不符合 OLAP 场景的 “确定性延迟” 需求。

四、关键原因 3:资源调度的 “提前控制” 逻辑,而非 “事后补救”

Doris 的资源管理(如资源组 Resource Group)遵循 “提前控制、快速失败” 的原则,而非 “事后排队补救”,核心目的是保证集群资源的隔离性和稳定性

  1. 资源组限制:通过资源组为不同用户 / 查询类型分配内存、CPU 配额(比如给 “adhoc 查询” 分配 30% 内存,给 “批量报表” 分配 50%),避免单个查询抢占过多资源;
  2. 内存预判:查询提交时,FE 会根据 Tablet 数量、数据量、算子类型(如是否有大表 Join)预判所需内存,如果超过资源组配额或全局内存限制,直接报错;
  3. 避免级联影响:如果允许 Tablet 排队,单个 “内存密集型查询” 可能长时间占用资源,导致其他正常查询(本可快速执行)也被迫排队,引发集群 “整体卡顿”,反而降低可用性。

Doris 认为:“内存不足报错” 是 “提前拦截不合理查询”,比 “让它排队拖累整个集群” 更合理 —— 报错后,用户可以优化查询(如加谓词过滤、拆分大表 Join)或调整资源配置,而不是让集群在 “无序排队” 中逐渐不可用。

五、总结:为什么不排队?—— 设计优先级决定的必然选择

Doris 不采用 Tablet 排队处理内存不足的核心逻辑,可以概括为三句话:

  1. 目标不允许:OLAP 场景需要 “秒级响应”,排队会让延迟失控,违背设计初衷;
  2. 架构不支持:MPP 的并行性依赖所有子任务同步完成,单个 Tablet 排队会破坏整体并行效率;
  3. 管理更安全:“提前报错” 能避免单个查询占用过多资源,保证集群资源隔离和稳定性,比 “事后排队补救” 更可控。

延伸:内存不足时,正确的优化方向(而非期待排队)

如果 Doris 频繁报 “内存不足”,不应期待 “排队” 功能,而应从以下角度优化:

  1. 优化查询本身

    • 加谓词过滤(WHERE子句),减少扫描的 Tablet 数据量;
    • 避免 “全表扫描 + 大表 Join”,优先用 “分区裁剪、分桶过滤” 缩小数据范围;
    • 拆分复杂查询(如将 “多表 Join + 聚合” 拆分为多个子查询,用中间表存储结果)。
  2. 调整资源配置

    • 为查询设置合理的mem_limit(如SET mem_limit = 4G),避免单个查询占用过多内存;
    • 优化 BE 节点的内存分配(如调整be.conf中的storage_mem_limitexec_mem_limit);
    • 通过资源组(Resource Group)为不同查询类型分配更合理的资源配额。
  3. 调整表结构

    • 优化分桶策略(如增加分桶数,减少单个 Tablet 的数据量,降低单次计算的内存需求);
    • 对大表进行合理分区(如按时间分区,查询时只扫描目标分区的 Tablet)。
1 Answers

后续会支持 spill to disk