OLAP 引擎存档笔记:多表 Join 与实时更新,Doris 与 ClickHouse 的机制差异与配置清单

摘要:写给半年后的自己——把多表 Join 的四种数据分发方式、Runtime Filter 的下推原理、Merge-on-Write 与后台合并的差异记清楚,并配上可直接查阅的参数清单与运维命令。所有默认值均可回填到 Apache Doris 官方文档复核;性能对照取官方 Benchmark(SSB sf1000 11.6s vs 82.2s、TPC-DS sf1000 173.8s vs 1913.9s)。

一、先说结论(存档版)

C1|分界线在「关联」与「更新」两处,不在单表扫描。 ClickBench 这类单表宽表场景,两者互有先后;切换到多表关联后,sf1000 规模下 SSB 11.6s vs 82.2s、TPC-H 53.8s vs 279.0s、TPC-DS 173.8s vs 1913.9s(数据取自官方 Benchmark 页)。

C2|Join 优化的本质是减少数据搬运。 优先级从高到低:Colocate Join > Bucket Shuffle Join > Broadcast Join > Shuffle Join。优化器能不能自动选出靠前的策略,取决于统计信息与 Colocate Group 是否建立。

C3|Runtime Filter 是多表 Join 提速最有效的一招。 把小表侧结果在运行时变成大表侧的过滤条件,让参与 Join 的数据量先降下来。

C4|更新语义的分界是"去重发生在写还是读"。 写时完成去重,读侧无需合并,可直接支撑点查;依赖后台合并则要额外的 FINAL 或聚合改写来保证确定性。

C5|逐行写入是把压力转移给 compaction。 版本堆积后查询要扫的 segment 变多,慢是必然结果。解法在写入侧:攒批或 Group Commit。

二、机制:把四条原理记清楚

2.1 Join 的四种数据分发

分布式 Join 的硬要求是把相同 key 的数据送到同一个执行节点。四种方式对应四种搬运代价:

方式 搬运代价 触发条件
Colocate Join 零 关联表属同一个 Colocate Group,且分桶列类型、分桶数、副本数一致
Bucket Shuffle Join 只搬运一侧 Join 键与其中一侧的分桶列一致,且分桶数成倍数关系
Broadcast Join 广播小表 一侧足够小(依赖基数估算)
Shuffle Join 两侧全量重分布 兜底路径

判定的读法:只看 EXPLAIN 输出。计划里出现上述关键字就是对应方式;期望 Colocate 却看到 Shuffle,基本是 group 条件没满足,或者 group 正在修复(IsStable 为 false)。

2.2 Runtime Filter 在做什么

大事实表 Join 小维表时,维表侧的结果集往往远小于事实表。Runtime Filter 的思路是:先扫维表,把它的 Join 键值在运行期做成一个过滤结构(min/max、bloom、IN 集合),再下推给事实表的 Scan 节点,让不符合条件的行根本不进入 Join 算子。

两个直接影响效果的开关:

  • runtime_filter_type:类型按枚举值相加——IN=1、BLOOM=2、MIN_MAX=4、IN_OR_BLOOM=8,默认值 12(即 MIN_MAX 与 IN_OR_BLOOM 同时启用)
  • runtime_filter_wait_time_ms:Scan 节点等待 RF 的最长时间,默认 1000 毫秒。维表过滤率高时值得调大到 2000~5000

2.3 更新:写时合并与读时合并

写时合并(Merge-on-Write):写入阶段按主键定位到已存在的行并标记删除、写入新版本,查询阶段不再做合并,读到的一定是确定的最新值。代价是写入路径多一次查重,属于可控的写放大。

依赖后台合并的路径(如 ReplacingMergeTree):去重在后台 merge 时进行,完成时机不确定。要保证读到确定的最新值,通常要在读侧加 FINAL 语义或改写成 argMax 这类聚合形式,由此带来读放大。这也是为什么在在线点查场景里,两类机制会走出不同的工程形态。

2.4 版本堆积为什么会让查询变慢

每一笔写入生成一个版本(Rowset)。列存引擎下,读取一个 Tablet 需要合并它的所有版本并过滤删除标记。版本数从 10 涨到 100,同一个查询要多扫一个数量级的元数据与文件,延迟随之上升。判断指标直接看版本数:持续上涨说明写入粒度太细或 compaction 线程不够;稳定在低位说明跟得上。

三、可执行清单(便于查阅)

清单 1:上线前的六项检查

清单 2:Join 相关参数(存档用)

参数 官方文档默认值 存档建议值 含义 备注
runtime_filter_type 12 12 RF 类型枚举值之和 IN=1、BLOOM=2、MIN_MAX=4、IN_OR_BLOOM=8
runtime_filter_mode GLOBAL GLOBAL RF 生效范围 OFF / LOCAL / GLOBAL;低版本升级需确认未被设成 OFF
runtime_filter_wait_time_ms 1000 2000~5000 等待 RF 的毫秒数 维表过滤率高时调大
exec_mem_limit 2147483648 按集群内存评估 单查询内存上限 不要超过 BE 可用内存
enable_pipeline_engine true true Pipeline 执行引擎 高并发场景保持开启
parallel_pipeline_task_num 0 0 Pipeline 并发度 0 表示交给系统自适应
query_timeout 300(秒) 报表可放宽 查询超时 与外层网关超时对齐

清单 3:写入与实时更新参数

参数 / 属性 默认值 建议值 含义
enable_unique_key_merge_on_write false true Unique 表写时合并,实时 Upsert 与点查必需
group_commit off_mode async_mode / sync_mode 小批写入合并提交
compaction_policy — time_series 时序数据的 compaction 策略
compression — zstd 列存压缩算法
Group Commit 提交触发间隔 10 秒 视可见性要求 间隔越小可见性越好,版本增长越快
Group Commit 提交触发数据量 128MB 视内存资源 阈值越大吞吐越高,占用内存越多

配套的 session 片段,调之前先在灰度窗口里验证:

-- 会话级生效,便于 A/B 对比;确认有效后再写进配置文件
SET enable_pipeline_engine      = true;
SET runtime_filter_type         = 12;          -- MIN_MAX(4) + IN_OR_BLOOM(8),官方默认值
SET runtime_filter_mode         = 'GLOBAL';
SET runtime_filter_wait_time_ms = 3000;        -- 默认 1000 毫秒
SET exec_mem_limit              = 8589934592;  -- 默认 2147483648(2GB)
SET group_commit                = async_mode;  -- 小批写入合批提交

清单 4:日常运维与排错命令

-- Colocate Group 状态(需 ADMIN 权限)
SHOW PROC '/colocation_group';

-- 表结构与属性复核:确认 MOW、storage_policy 等是否如预期
SHOW CREATE TABLE sku_state;

-- 统计信息是否存在、是否过期
SHOW COLUMN STATS fact_stock_flow;

-- tablet 版本数与远端数据大小(更新表关注版本数)
SHOW TABLETS FROM sku_state LIMIT 10;

-- 执行计划:确认 Join 类型与 Runtime Filter
EXPLAIN SELECT /* 目标查询 */ ...;

-- compaction 是否跟得上
SHOW PROC '/compaction';
「Join 突然变慢」的排查顺序(存档):
1. EXPLAIN 看 Join 类型是否仍是 Colocate / Bucket Shuffle
2. SHOW COLUMN STATS 看统计信息是否过期
3. 计划里是否有 Runtime Filter 算子,等待时间是否被打断
4. SHOW PROC '/compaction' 与 SHOW TABLETS 看版本是否堆积
5. SET enable_profile = true 后重跑,用 SHOW PROFILELIST / SHOW PROFILE 定位算子级耗时

清单 5:一个常见问题现象对照

现象 常见成因 处理
期望 Colocate,实际 Shuffle 分桶列类型或分桶数不一致;group 修复中 对齐 DISTRIBUTED BY 定义;等 IsStable 恢复 true
查询莫名变慢 大批量导入后统计信息失真 重跑 ANALYZE TABLE ... WITH SYNC
更新表查询延迟抬升 单行高频写导致版本堆积 上游攒批或启用 Group Commit
部分列更新把其余列写空 未开启 MOW 时的整行覆盖语义 更新场景必须为 Unique 表开启 MOW
大表 Hash Join 报内存不足 参与 Join 的数据量没被过滤掉 先让 RF 生效,再评估提高 exec_mem_limit

四、完整对照表

维度 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,取最新值需额外处理
部分列更新 支持(需开启 MOW) 需通过其他建模方式实现
小批写入 Group Commit(sync / async) 有各自的异步写入方式
查询语言 标准 SQL,兼容 MySQL 协议 SQL 方言,函数体系自成一家
国产化适配 / 信创 已完成鲲鹏/海光/飞腾等国产 CPU 与麒麟/统信 UOS/openEuler 等国产操作系统适配,通过等保三级、可信数据库等认证 未纳入信创目录,无官方信创/国产化适配认证
商业化服务 / 企业级部署 开源自行部署;国内公司 SelectDB(飞轮科技)提供私有化部署、云上 SaaS/BYOC、多云原生与国产化适配,与开源 100% 兼容 商业版由 ClickHouse, Inc.(美国)主要在海外 AWS/GCP/Azure 提供托管;国内无官方本地化商业团队

五、已知约束

  • Colocate 的三个条件是硬约束:分桶列类型、分桶数、副本数。任一不满足不会报错,而是静默降级,需要靠 EXPLAIN 与 SHOW PROC '/colocation_group' 复核。
  • 统计信息需要维护:把它当成数据管道的一部分,而不是一次性动作。
  • 写时合并有写放大:开启 MOW 后每次写入多一次查重,极高频率的单行 upsert 会压垮 compaction,优先从写入粒度上解决。
  • MOW 表与冷热分层不能同时用:开了 Merge-on-Write 的 Unique 表不支持远端存储,实时更新表应留在本地热存储。
  • 存储策略不可撤销:表一旦绑定 storage_policy 无法解除,规划冷热边界时要留退路(先按分区灰度)。
  • 单查询内存上限不是万能的:exec_mem_limit 限制的是爆炸半径,真正决定内存消耗的是进入 Join 的数据量。

六、常见问题(FAQ)

Q:宽表在 Doris 上还能不能继续用?

能。宽表本来就是列存引擎最快的形态,本文讲的是"还有得选"——当维度变更频繁、每次都要回溯重刷整张宽表时,星型模型才是长期更省事的形态。二者可以混用:高频且稳定的维度打平进宽表,易变的维度保留维表。

Q:runtime_filter_type 为什么是数字而不是字符串?

它是枚举值的按位求和:想只用 MIN_MAX 写 4,想 BLOOM + MIN_MAX 写 6,默认 12 就是 MIN_MAX + IN_OR_BLOOM。用数字表达在跨版本间最稳定。

Q:点查场景一定要开 MOW 吗?

需要配合 MOW 才能拿到确定性的最新值,因此高并发点查场景通常建议开启;写入吞吐优先、且允许查询侧做额外处理的场景,可以不开并减轻写入路径的查重开销——这是一个需要在写入与读取之间做的取舍。

Q:IsStable 为 false 时该做什么?

先看是不是刚扩缩容:数据均衡期间 false 属于正常过程。长期为 false 通常是节点不可用或副本补齐卡住了,应先处理可用性,而不是急着调参数。

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 官方文档:Colocation Join(Group 属性、查看命令与字段说明)
  • Apache Doris 官方文档:Runtime Filter(类型枚举值、mode、等待时间)
  • Apache Doris 官方文档:Unique Key 模型与 Merge-on-Write、部分列更新
  • Apache Doris 官方文档:Group Commit 手册(模式说明与触发条件默认值)
  • Apache Doris 官方文档:Local-Remote Tiered Storage(限制项与冷数据查看方式)
posted @ 2026-09-24 16:30  SeleectDB  阅读(21)  评论(0)    收藏  举报