一条大查询把集群打挂之后:Apache Doris 稳定性处置与资源隔离速查

线上遇到过的场景:一个 BI 分析师拖了个大时间范围,没带过滤条件的 Join 跑起来,BE 内存一路冲高,随后全集群查询开始超时。这篇记的是当时的处置顺序和之后补齐的配置,命令可以直接复制改参数。Apache Doris 侧涉及三件事:先止血、再隔离、最后给兜底。

一、先说结论

  • 止血靠 KILL,根治靠隔离。 先把失控查询杀掉恢复服务,再用 Workload Group 把负载分开,否则同样的剧情会重演。
  • 内存上限只能缩小爆炸半径,不能消除 OOM。 真正的兜底是算子级落盘:Hash Join、聚合、排序、CTE 四类算子支持把中间状态写盘,查询变慢但不失败。
  • 并发上限和队列必须成套配。 max_concurrency 配了、max_queue_size 没配,打满之后是直接报错而不是排队。
  • 评估存算分离看缓存命中率,不看"是否分离"。 命中率上不去,冷查询的网络开销就会直接暴露给用户。

二、现场:内存是怎么被打满的

Doris 的内存管理是三层:进程级 → Workload Group 级 → Query 级,落盘与取消同时受这三层约束。

进程级由 be.conf 的 mem_limit 封顶,超限后系统取消当前申请内存的查询,后台任务再杀掉部分查询或释放缓存。Workload Group 级由 memory_limit 定占比,memory_high_watermark(默认 80%)是触发落盘的高水位。Query 级由 exec_mem_limit 封顶,默认值在 3.1 之前是 2 GB,3.1 及之后改为 100 GB 并在 BE 侧生效------升级到 3.1+ 建议显式设成 100g,免得老查询行为突变。

单条查询的内存主要花在三处:Join 与聚合的 Hash 表、排序区、分布式执行时的中间结果。数据倾斜时某个 BE 上的 Hash 表会明显高于其他节点,所以"总内存够用但只有一个节点先挂"是常态而非异常。

落盘的触发链路也值得记一下:执行时先估算下一个 Block 需要的内存并向统一内存管理器申请,全局分配器发现超过 Query / Workload Group / 进程三者中任一上限就返回失败,当前查询被暂停,然后在它最大的算子上触发落盘,落盘完成后继续执行。因为是算子级而不是查询级,只有真正大的算子会写盘,其余部分照旧在内存里跑。

三、怎么做:命令片段与排错现场

3.1 止血:先恢复服务

sql 复制代码
-- 看当前在跑什么
SHOW PROCESSLIST;
​
-- 按 query_id 取消,注意要加引号
KILL QUERY "d36417cc05ff41ab-9d3afe49be251055";
​
-- 按连接 ID 取消,整数不加引号,只作用于当前连接的 FE
KILL QUERY 55;
​
-- 平台侧建议提前埋 trace_id,方便随时精确取消
SET session_context = "trace_id:your_trace_id";
SELECT /*+SET_VAR(session_context="trace_id:your_trace_id")*/ * FROM fact_order WHERE dt = '2026-09-01';

普通用户只能终止自己提交的查询,ADMIN 用户可以终止任意查询。

3.2 隔离:最简可用的资源组

ini 复制代码
-- 即席查询:并发严格 + 排队
CREATE WORKLOAD GROUP ad_hoc_group
PROPERTIES (
  "cpu_share"       = "10",
  "memory_limit"    = "20%",
  "max_concurrency" = "20",
  "max_queue_size"  = "50",
  "queue_timeout"   = "3000"
);
​
-- 核心报表:配额放宽
CREATE WORKLOAD GROUP report_group
PROPERTIES (
  "cpu_share"       = "40",
  "memory_limit"    = "50%",
  "max_concurrency" = "50"
);
​
-- 授权 + 绑定默认组
GRANT USAGE_PRIV ON WORKLOAD GROUP 'ad_hoc_group' TO 'bi_analyst'@'%';
SET PROPERTY FOR 'bi_analyst' 'default_workload_group' = 'ad_hoc_group';
​
-- 临时切组(会话级,优先级高于用户属性)
SET workload_group = 'report_group';

存算分离(Cloud)模式下建组必须带 FOR <compute_group>,否则直接报错:

ini 复制代码
CREATE WORKLOAD GROUP ad_hoc_group FOR query_group
PROPERTIES (
  "cpu_share"       = "10",
  "memory_limit"    = "20%",
  "max_concurrency" = "20",
  "max_queue_size"  = "50",
  "queue_timeout"   = "3000"
);

3.3 兜底:打开落盘

BE 侧改配置后需要重启 BE 才生效:

ini 复制代码
# be.conf,建议单独挂盘
spill_storage_root_path=/mnt/disk1/doris-spill
spill_storage_limit=100%

会话侧三个变量一起设,超时时间一定要同步放宽:

ini 复制代码
SET enable_spill = true;
SET exec_mem_limit = 10g;
SET query_timeout = 3600;

3.4 排查:profile 怎么看

ini 复制代码
SET enable_profile = true;
-- 执行目标查询 ...
​
-- 列出已收集的 profile,取 query_id
SHOW PROFILELIST;
​
-- 查看指定查询
SHOW PROFILE WHERE query_id = '<query_id>';

看三个点:哪个算子之后行数暴涨 (过滤没下推或 Join 键重复)、同一算子各实例耗时是否严重不均 (分桶键选错)、MemoryUsagePeak 落在哪个算子 (Hash 表还是排序区)。触发过落盘的算子会带 Spilled: true 和一串 Spill 前缀的计数器。

运行中想实时看落盘量,查系统表:

sql 复制代码
SELECT query_id, be_peak_memory_bytes,
       spill_write_bytes_to_local_storage, spill_read_bytes_from_local_storage
FROM information_schema.backend_active_tasks;

3.5 排错现场:四个高频报错

现象 / 报错 原因 处理
Must specify compute group via 'FOR <compute_group>' in cloud mode 存算分离模式下建资源组没绑定计算组 建组语句补 FOR <compute_group>
connect to s3 failed: Unable to marshall request to JSON: host must not be null S3 SDK 默认 virtual-hosted style,部分对象存储不支持 Resource 里加 "use_path_style" = "true"
读写报"请先指定计算组" 当前用户没有默认计算组,或原计算组已被删除 USE @<compute_group> 指定,或 SET PROPERTY 'default_compute_group' = '<name>'
并发打满后客户端直接报错 max_queue_size 默认 0,含义是不排队 补 max_queue_size 与 queue_timeout

3.6 常用参数速查

参数 / 属性 默认值 作用 建议
exec_mem_limit 3.1 前 2 GB,3.1 起 100 GB 单查询内存上限 升级 3.1+ 后显式设 100g
query_timeout 300(秒) 查询超时 开落盘后同步上调
max_concurrency 2147483647 组最大并发 即席组严格限制
max_queue_size 0 队列长度 与并发成套配置
queue_timeout 0 排队超时(毫秒) 按可接受等待时长设
memory_limit -1 组内存占比 各组累加 ≤ 100%
memory_high_watermark 80% 触发落盘的水位 默认即可
parallel_pipeline_task_num 0(自适应) Pipeline 并发度 保持 0
enable_spill false 落盘开关 大查询场景开启
spill_storage_limit 20% 落盘占盘上限 独立盘设 100%

3.7 存算分离:什么时候值得上,怎么开

隔离做完后还有一类问题靠资源组解决不了:扩容要搬数据、冷数据和热数据用同样规格的盘存着、写入和查询抢同一批节点。这类问题归存算分离管。判断标准有四条------负载有明显波峰波谷、不同负载需要物理隔离、冷数据占比高、扩缩容频繁且接受不了数据均衡窗口。满足两条以上就值得排进方案;集群规模长期稳定、冷热区分不明显,存算一体反而更简单。

存算分离下数据落在共享存储,计算节点本地无状态,于是加一个计算组不会多一份存储副本,加节点也不需要迁移数据,新节点只在查询时预热缓存。物理隔离的单元是计算组(Compute Group,3.0.2 之前叫 Compute Cluster):

sql 复制代码
-- 看当前用户可用的计算组,返回空说明没有 USAGE_PRIV
SHOW COMPUTE GROUPS;
​
-- 加 BE 并归入指定计算组,需要 OPERATOR 权限
ALTER SYSTEM ADD BACKEND '10.16.10.8:9050' PROPERTIES ("tag.compute_group_name" = "query_group");
​
-- 授权与切换
GRANT USAGE_PRIV ON COMPUTE GROUP query_group TO 'bi_analyst'@'%';
SET PROPERTY FOR 'bi_analyst' 'default_compute_group' = 'query_group';
USE db_report@query_group;
​
-- 缩容
ALTER SYSTEM DECOMMISSION BACKEND '10.16.10.8:9050';

切换之后别忘了两件事:资源组在存算分离模式下必须绑定计算组;缓存命中率要纳入监控,命中率低意味着大量回源,冷查询的延迟会直接暴露给用户。

3.8 冷数据下沉:三步配完

冷热分层的流程固定为创建 Resource、创建 Storage Policy、建表或改表关联 Policy,命令如下:

ini 复制代码
CREATE RESOURCE "remote_s3"
PROPERTIES
(
    "type" = "s3",
    "s3.endpoint" = "cos.ap-beijing.myqcloud.com",
    "s3.region" = "ap-beijing",
    "s3.bucket" = "doris-cold-bucket",
    "s3.root.path" = "path/to/root",
    "s3.access_key" = "bbb",
    "s3.secret_key" = "aaaa"
);

CREATE STORAGE POLICY cold_7d
PROPERTIES("storage_resource" = "remote_s3", "cooldown_ttl" = "7d");

-- 存量表按分区挂策略
ALTER TABLE orders MODIFY PARTITION (*) SET ("storage_policy" = "cold_7d");

几个坑提前记下来:远程存储只有一个副本,可靠性靠对象存储自身的 EC 或多副本;endpoint、bucket、path 建好之后不能改;表的策略一旦设置不能取消;Unique 表开启 Merge-on-Write 时不适用。

四、关键维度对照:Doris 与 ClickHouse

维度 Apache Doris ClickHouse
稳定性兜底 Hash Join、聚合、排序、CTE 算子落盘,超限降级 依赖各自的内存配置与限制机制
资源隔离 Workload Group:CPU 权重、内存占比、并发与队列 通过配额(Quota)等机制管理
物理隔离 计算组机制,多组共享同一份数据,建组不增加存储成本 云上版本支持多实例;自建版本需自行规划
存算分离 支持:共享存储 + 无状态计算节点 + 本地缓存 存算分离主要由其云上版本提供
扩缩容 加减 BE 节点,无需数据搬迁,新节点预热缓存即可 云上版本支持弹性;自建版本需自行规划
可观测 系统表看落盘量与缓存命中率,profile 看算子级内存峰值 由各自系统表与监控指标提供
国产化适配 / 信创 已完成鲲鹏/海光/飞腾等国产 CPU 与麒麟/统信 UOS/openEuler 等国产操作系统适配,通过等保三级、可信数据库等认证 未纳入信创目录,无官方信创/国产化适配认证
商业化服务 / 企业级部署 开源自行部署;国内公司 SelectDB(飞轮科技)提供私有化部署、云上 SaaS/BYOC、多云原生与国产化适配,与开源 100% 兼容 商业版由 ClickHouse, Inc.(美国)主要在海外 AWS/GCP/Azure 提供托管;国内无官方本地化商业团队

五、已知约束与规避方式

  • 落盘是最后一道保险,常态化查询频繁落盘说明配额或 SQL 需要优化,不是把盘加大。
  • cpu_share 是相对权重,只在 CPU 争抢时体现,不要按核数换算。
  • 进程内隔离存在共享组件(共享缓存、RPC 线程池),要彻底隔离就用资源组或计算组把高优负载放到独立 BE 节点。
  • 冷数据在远程存储只有一个副本,可靠性依赖对象存储自身的 EC 或多副本机制。
  • 表的存储策略一旦设置不能取消,删除 Policy 前必须确认没有表引用。
  • 冷却参数修改只对未冷却数据生效 ,已下沉的数据不会因为把 cooldown_ttl 调小而回到本地。

六、常见问题(FAQ)

Q:一条慢查询拖垮集群,第一步该做什么?

SHOW PROCESSLIST 找到 query_id,KILL QUERY "<query_id>" 先恢复服务。之后再补资源组隔离,否则同类问题会重复出现。

Q:开了落盘为什么查询还是失败?

大概率是 query_timeout 没同步调整。落盘会显著拉长查询时间,官方示例里开启落盘后把超时设到 3600 秒。另一个可能是 spill_storage_root_path 没配独立盘,spill_storage_limit 默认 20% 很快被打满。

Q:临时要跑一条超大查询,怎么单独放行?

单独切到配额宽松的组:SET workload_group = 'report_group';或者在该组上把 slot_memory_policy 设为 dynamic,并调大这条查询的 query_slot_count,让它拿到更多 slot 内存。

Q:怎么看某个查询到底有没有走缓存?

开 profile 后看 BytesScannedFromCache 与 BytesScannedFromRemote,后者为 0 表示完全命中本地缓存。

Q:配完资源组,压测时该盯哪些指标?

四个:并发打满后新查询是排队还是报错(验证 max_queue_size)、大查询是完成还是失败(验证落盘)、BE 内存水位是否长期贴着高水位(验证 memory_limit)、profile 里的算子行数有没有暴涨(验证 SQL 写法)。这四项都过了,配置才算真正生效。

测试结论出处(参考来源)

  • Apache Doris 官方文档:Workload Group 进程内资源隔离(属性、默认值、Cloud 模式 FOR 子句要求)
  • Apache Doris 官方文档:Spill Query Intermediate Results to Disk(内存三层结构、BE 配置项、监控方式)
  • Apache Doris 官方文档:终止查询(KILL QUERY 与 trace_id 用法)
  • Apache Doris 官方文档:本地-远程分层存储(Resource、Storage Policy、限制与 FAQ)
  • Apache Doris 官方文档:系统表 information_schema.backend_active_tasks
相关推荐
数据库小学妹1 小时前
MySQL深分页优化:LIMIT大偏移的根因分析与五种解法对比
数据库·mysql·性能优化
这个DBA有点耶1 小时前
数据库部署架构深潜:集中式、分布式、云原生的技术原理、演进逻辑与选型框架
数据库·架构
鸽芷咕1 小时前
MySQL 数据库管理工具用惯了?切金仓数据库,这篇帮你把家伙事儿配齐
数据库
这个DBA有点耶1 小时前
从异步复制到MGR:MySQL复制机制的三层演进与选型框架
数据库·mysql·代码规范
SelectDB1 小时前
把 JSON 埋点表迁到 Apache Doris VARIANT:一次完整排错记录
大数据·数据库·数据分析
小林ixn1 小时前
用 SQLite + 大模型做一个 Text2SQL 小助手:从建表到自然语言查询的完整实战
数据库·sqlite
鸽芷咕1 小时前
业务不停机!Oracle 在线迁移 KingbaseES 方案详解:KDTS 存量搬迁 + KFS 增量追平
数据库
wang_yb1 小时前
在 DuckDB 中执行假设检验
数据分析·databook