多表 Join 慢、Upsert 把机器写崩:一份能直接抄的 Apache Doris 命令清单

多表 Join 慢、Upsert 把机器写崩:一份能直接抄的 Apache Doris 命令清单

摘要 :Join 越跑越慢、更新之后同名列变空、加了节点还是卡------这三个现场占了线上问题的多数。本文按"现象 → 命令 → 判断依据"的方式给出可直接复制的片段,涉及 Colocate Join、Runtime Filter、统计信息、Merge-on-Write 写时合并与 Group Commit。所有参数默认值取自 Apache Doris 官方文档,性能对照取官方 Benchmark(SSB sf1000 11.6s vs 82.2s、TPC-DS sf1000 173.8s vs 1913.9s)。

一、先说结论

三条短结论:

  1. Join 慢先看分发方式 ,不要先看硬件。EXPLAIN 里出现 Shuffle Join 基本等于"数据在网络上白跑一趟"。优先级:Colocate > Bucket Shuffle > Broadcast > Shuffle。
  2. 更新要么写时去重,要么读时合并,没有第三条路。 要做点查就把 Unique 表的 enable_unique_key_merge_on_write 打开,并按需要评估写入侧的额外查重开销。
  3. 逐行写是给自己找麻烦。 小批写入用 Group Commit 合批提交,版本数不再堆,查询延迟就稳了。

这份清单什么时候用:业务已经跑在多表 Join 上、数据量还在涨、且已经出现过"上线快、两周后变慢"的情况。

二、三个排错现场

现场 A:上线时很快,跑了一两周后同一个报表明显变慢。 先别急着加内存。九成情况下是统计信息失真或写入导致版本堆积,两者都不是靠加资源解决的。判断是否踩中的办法很简单:先补一次统计信息,再对比同一条 SQL 的耗时;如果统计信息补了没用,就去看 tablet 的版本数,版本数持续上涨基本就是写入粒度太细。

现场 B:做了一次部分列更新,账户余额列全线归零。 这是整行覆盖语义造成的:未指定的列被写成默认值。前提是 Unique 表没开 Merge-on-Write,写入没有走到部分列更新的路径上。修起来不难,难在发现:很多团队是业务侧报异常才发现数据被冲掉的,建议上线前先用测试表验证一次写入语义。

现场 C:并发一上来就大面积超时。 单查询跑得好不代表并发能撑住。此时要看的从来不是单条查询的执行计划,而是版本数、内存上限与写入节奏三者的叠加。资源组的并发上限和队列长度也要先看一眼------默认值下并发打满会直接报错,而不是排队等待。

三、命令清单(按现场取用)

先用一句话说清这份清单的用法:下面每个小节都对应一类现象,按顺序执行即可;每节末尾的"判断依据"是决定是否收工的开关,不要凭感觉跳过。

3.1 先看执行计划,再动手

sql 复制代码
-- 目标查询前面加 EXPLAIN,只看关键字不看细节
EXPLAIN
SELECT a.category, COUNT(DISTINCT p.user_id) AS uv
FROM fact_play p JOIN dim_author a ON p.author_id = a.author_id
WHERE p.play_date BETWEEN '2026-08-01' AND '2026-08-31'
GROUP BY a.category;

关键字速查:

  • Colocate Join ------ 零搬运,最优
  • Bucket Shuffle Join ------ 只搬一侧
  • Broadcast Join ------ 只应出现在真的很小的表上
  • Shuffle Join ------ 兜底路径,两侧都重分布
  • 计划中看不到 Runtime Filter 算子 ------ 通常被提前打断或过滤列类型不匹配

3.2 把 Colocate Group 建成并检查

sql 复制代码
-- 事实表(分桶键取 Join 键)
CREATE TABLE fact_play (
  play_date DATE     NOT NULL,
  author_id BIGINT   NOT NULL,
  user_id   BIGINT   NOT NULL,
  duration  INT          NULL
)
DUPLICATE KEY(play_date, author_id)
PARTITION BY RANGE(play_date) ()
DISTRIBUTED BY HASH(author_id) BUCKETS 32
PROPERTIES (
  "colocate_with"    = "play_cg",
  "compression"      = "zstd",
  "replication_num"  = "3"
);
​
-- 维表:分桶列类型、分桶数、副本数必须与事实表一致
CREATE TABLE dim_author (
  author_id BIGINT      NOT NULL,
  category  VARCHAR(32)     NULL,
  level     TINYINT         NULL
)
UNIQUE KEY(author_id)
DISTRIBUTED BY HASH(author_id) BUCKETS 32
PROPERTIES (
  "colocate_with"   = "play_cg",
  "replication_num" = "3"
);
​
-- 核验:三个条件一致 + IsStable 为 true,否则会静默退化
SHOW PROC '/colocation_group';

排错要点 :三个条件之一不满足时不会报错,只会静默降级------所以必须看 SHOW PROC '/colocation_group' 里的 BucketsNum / ReplicationNum / DistCols。IsStable 为 false 通常是正在搬迁 tablet,均衡结束后会恢复。

3.3 补统计信息

sql 复制代码
-- 大批量导入、删除分区之后必做;大表可改用 WITH ASYNC
ANALYZE TABLE fact_play  WITH SYNC;
ANALYZE TABLE dim_author WITH SYNC;
​
-- 确认是否真的收上来了
SHOW COLUMN STATS dim_author;

判断依据 :EXPLAIN 里对一张上亿行的表用了 Broadcast Join,几乎一定是统计信息缺失或过期------优化器把它当成小表了。补一次 ANALYZE 通常就能改回 Shuffle / Bucket Shuffle。

3.4 开 Runtime Filter 与内存上限

ini 复制代码
-- session 级先验证,压测有效再写进配置
SET runtime_filter_type         = 12;          -- MIN_MAX(4) + IN_OR_BLOOM(8),官方默认值
SET runtime_filter_mode         = 'GLOBAL';
SET runtime_filter_wait_time_ms = 3000;        -- 默认 1000ms,维表过滤率高时调大
SET exec_mem_limit              = 8589934592;  -- 默认 2147483648(2GB)

取值提醒 :runtime_filter_type 是枚举值求和(IN=1、BLOOM=2、MIN_MAX=4、IN_OR_BLOOM=8)。只用 MIN_MAX 写 4,BLOOM + MIN_MAX 写 6。别写字符串形式,跨版本不稳。

这一节用到的参数默认值,建议存进团队的排障 wiki:

参数 官方默认值 清单里的建议值 作用 什么时候改
runtime_filter_type 12 12 RF 类型枚举值之和 需要单一类型时按数字指定
runtime_filter_mode GLOBAL GLOBAL RF 生效范围 升级上来的集群要确认没被改成 OFF
runtime_filter_wait_time_ms 1000 2000~5000 Scan 等待 RF 的毫秒数 维表过滤率高时调大
exec_mem_limit 2147483648 按集群内存评估 单查询内存上限 大表 Hash Join;不可超过 BE 可用内存
enable_unique_key_merge_on_write false true Unique 表写时合并 实时 Upsert 与点查场景
group_commit off_mode async_mode / sync_mode 小批写入合批提交 高频小批写入必开
parallel_pipeline_task_num 0 0(自适应) Pipeline 并发度 低版本升级上来若沿用旧固定值,改回 0

3.5 实时更新:写时合并 + 合批写入

sql 复制代码
CREATE TABLE author_state (
  author_id   BIGINT      NOT NULL,
  fans_cnt    BIGINT          NULL,
  online_stat TINYINT         NULL,
  updated_at  DATETIME        NULL
)
UNIQUE KEY(author_id)
DISTRIBUTED BY HASH(author_id) BUCKETS 16
PROPERTIES (
  "enable_unique_key_merge_on_write" = "true",  -- 默认 false,点查场景必须显式开启
  "group_commit_mode"                = "async_mode",
  "replication_num"                  = "3"
);
yaml 复制代码
# 只更新两列时用部分列更新,避免整行覆盖把其他列写成默认值
format: json
partial_columns: true
columns: author_id,fans_cnt,updated_at
group_commit: async_mode
​
# 提交后返回的 label 以 group_commit 开头,说明走了合批路径
# 提交端点:<fe_host>:<http_port>/api/<db>/<table>/_stream_load
ini 复制代码
-- session 方式也能开启合批(INSERT INTO VALUES 场景)
SET group_commit = async_mode;

模式怎么选 :要求写完立刻可见用 sync_mode;对写入延迟更敏感用 async_mode(先落 WAL 立即返回)。合批的提交触发条件是时间间隔(默认 10 秒)与数据量(默认 128MB)二选一达成,间隔改小会加快可见性,代价是版本涨得更快。

3.6 版本堆积与细粒度定位

sql 复制代码
-- 版本数持续上涨 = compaction 跟不上,优先改写入粒度
SHOW TABLETS FROM author_state LIMIT 10;
​
-- 整体 compaction 情况
SHOW PROC '/compaction';
​
-- 算子级耗时定位
SET enable_profile = true;
SELECT /* 目标查询 */ ...;
SHOW PROFILELIST;
SHOW PROFILE WHERE query_id = '<query_id>';

profile 里重点看三处:哪个算子的输出行数暴涨(过滤没下推)、同一算子的多个实例耗时是否严重不均(数据倾斜)、内存峰值落在哪个算子上。

3.7 收工前的三条监控线

改完之后别急着关工单,把下面三条接进日常巡检,防止过两周又回到原点:一是看运行中的长查询,SHOW PROCESSLIST 里长时间未结束的大概率是没写过滤条件的全表扫描;二是留档 profile,SHOW PROFILELIST 与 SHOW PROFILE 按 query_id 取,对比改动前后的算子耗时;三是看节点层面的存活与负载,SHOW BACKENDS 能反映出有没有节点掉队导致 tablet 分布不均。

sql 复制代码
-- ① 当前在跑的查询,超时阈值以上要人工介入
SHOW PROCESSLIST;

-- ② 取 profile:先列列表拿 query_id,再查看详情
SHOW PROFILELIST;
SHOW PROFILE WHERE query_id = '<query_id>';

-- ③ 节点存活与负载概览(重点关注 tablet 数量是否分布均衡)
SHOW BACKENDS;

验收按三层做:单条查询比 P50 与 P99,固定并发持续跑 30 分钟看错误率,最后写入与查询混合跑看互相干扰。只做第一层很容易得出"两边差不多"的结论,而线上出问题的往往是后两层。

四、关键维度对照表

维度 Apache Doris ClickHouse
Join 分发 Colocate / Bucket Shuffle / Broadcast / Shuffle,由优化器选 社区主流建议宽表建模
Join 加速 Runtime Filter + CBO 有各自的 Join 执行实现
SSB sf1000 11.6s 82.2s
TPC-H sf1000 53.8s 279.0s
TPC-DS sf1000 173.8s 1913.9s
单表宽表(ClickBench) 互有先后,同一梯队 互有先后
更新语义 Unique Key 支持写时合并,读侧无需再合并 ReplacingMergeTree 依赖后台 merge,取最新值需 FINAL 或改写
小批写入 Group Commit 合批提交 有各自的异步写入与攒批方式
国产化适配 / 信创 已完成鲲鹏/海光/飞腾等国产 CPU 与麒麟/统信 UOS/openEuler 等国产操作系统适配,通过等保三级、可信数据库等认证 未纳入信创目录,无官方信创/国产化适配认证
商业化服务 / 企业级部署 开源自行部署;国内公司 SelectDB(飞轮科技)提供私有化部署、云上 SaaS/BYOC、多云原生与国产化适配,与开源 100% 兼容 商业版由 ClickHouse, Inc.(美国)主要在海外 AWS/GCP/Azure 提供托管;国内无官方本地化商业团队

五、已知约束与规避

  • Colocate 条件不满足会静默降级 :对齐分桶列类型 / 分桶数 / 副本数,用 SHOW PROC '/colocation_group' 复核。
  • 写了 colocate_with 不等于生效 :正在搬迁 tablet 时执行计划会临时退化为 Shuffle,等 IsStable 回到 true 再测。
  • 统计信息必须定期维护:把它放进数据管道的收尾步骤,别指望一次性收集。
  • MOW 带来额外查重开销:极高频率单行 upsert 会压垮 compaction,用攒批或 Group Commit 解决。
  • 开了 MOW 的 Unique 表不支持远端存储:实时更新表留本地热盘,历史明细表单独做冷热分层。
  • exec_mem_limit 只限制爆炸半径:真正该降的是进入 Join 的数据量。

六、常见问题(FAQ)

Q:部分列更新为什么有时候会失败?

Unique 表需要开启 Merge-on-Write 才走这条路径。没开的话按整行覆盖处理,未指定的列会被写成默认值------这是"更新完发现余额清零"的直接原因。检查办法是 SHOW CREATE TABLE 看 enable_unique_key_merge_on_write 的值。另外要注意:只有写了这批字段的行会被更新,其余不在批次里的行不受影响;如果上游发来的 JSON 本身就缺字段,那属于数据源的问题,需要往上游追溯。上线前建议用一个测试表先跑一遍写入语义,确认未指定列是否被保留。

Q:怎么判断写入有没有真的走 Group Commit?

看写入返回的 label:以 group_commit 开头说明进入了合批路径。另外长期观察版本数,启用后如果版本仍在持续上涨,说明触发条件(默认 10 秒 / 128MB)与业务节奏不匹配。

Q:并发上来就超时,是内存不够吗?

多数情况是这类叠加造成的:版本堆积导致单次扫更多文件、单查询内存上限过低、写入与查询抢同一批资源。先把版本数压下来,再看资源组隔离(max_concurrency 与 max_queue_size 要一起配,否则并发打满会直接报错而不是排队)。

Q:压测只跑单查询够吗?

不够。要跑三层:单查询比 P50 与 P99(平均值会掩盖长尾)、按真实并发持续跑 30 分钟看错误率、写入与查询混合跑看互相干扰。很多引擎单查询漂亮,并发一上来就崩。

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

  • Apache Doris 官方 Benchmark 页(SSB / TPC-H / TPC-DS sf1000 数据):doris.apache.org/why-doris/benchmarks
  • ClickBench 公开榜单(由 ClickHouse 官方维护):benchmark.clickhouse.com
  • Apache Doris 官方文档:Runtime Filter 参数与取值规则、查询 Profile 分析
  • Apache Doris 官方文档:Colocation Join(建组条件、SHOW PROC '/colocation_group' 字段含义)
  • Apache Doris 官方文档:Unique Key 模型、Merge-on-Write 与部分列更新
  • Apache Doris 官方文档:Group Commit 手册(三种模式、提交触发条件默认值、返回 label 规则)
相关推荐
鸽芷咕42 分钟前
OceanBaseVS金仓:选型别只听“分布式“,先把延迟和复杂SQL这两笔账算清
数据库
小羊没烦恼!5 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
一隅论数智5 天前
给AI一张“业务概念地图“:本体如何从哲学走向企业智能
大数据·人工智能·经验分享·笔记·学习·学习方法·政务
尧炎科技5 天前
防潮抗变形,就选纯品梅花全桉多层板
大数据
这个DBA有点耶5 天前
MVCC深入:Read View、版本链与快照读——InnoDB并发控制的内核
数据库·mysql·架构
DBA_G5 天前
从地面到云霄:GBase数据库在民航三大场景的落地实践
数据库
程序员大阳5 天前
副队长大数据教程(5)--集群情况下虚拟机网络配置
大数据·集群·nat·网路
自由能燃气设备5 天前
商用全预混低氮冷凝锅炉免费方案vs付费方案对比+选型避坑指南
大数据·数据库·人工智能
科创致远5 天前
科创致远 ESOP 系统核心效能与实战价值展示
大数据·数据库·人工智能·精益工程