3. 金融数据模型设计与表引擎选型
在 ClickHouse 金融数据面试中,表引擎选型几乎是必问题。面试官真正想确认的不是你能否背出四个引擎的定义,而是你能否把业务语义、数据变更方式、查询模式三者映射到正确的引擎上。金融场景里,行情、订单、成交、持仓、资金流水、风控指标各有不同的“更新语义”,选错引擎往往不是性能问题,而是结果正确性问题。
一、核心原理:四个引擎解决的是四种“变更语义”
ReplacingMergeTree 解决“同一主键只保留最新版本”。它按 ORDER BY 主键去重,可指定版本列,后台合并时保留版本最大的一行。注意:去重发生在 merge 阶段,查询时若未合并仍可能看到重复,因此必须配合 FINAL 或 argMax 使用。
SummingMergeTree 解决“同一维度多行数值相加”。合并时按主键把数值列求和,非数值列取任意一行。它适合可加性指标,如成交量、成交额、手续费,但不能处理不可加指标,如均价、最大值、去重人数。
AggregatingMergeTree 解决“预聚合复杂状态”。它存储 AggregateFunction 中间状态,配合物化视图写入,查询时用 merge 或 -Merge 组合器还原。适合 UV、分位数、加权均价等不可加指标。
CollapsingMergeTree 解决“行级撤销”。每行带 Sign 标记,1 表示有效,-1 表示撤销,合并时成对抵消。它适合订单撤单、流水冲正等场景,但要求写入严格成对,否则会残留脏数据。
二、方法流程:从业务语义到引擎选型
- 识别主键与更新频率:行情快照按 symbol+ts 高频覆盖,订单状态按 order_id 低频变更。
- 判断是否需要历史版本:风控审计要留痕,用 Collapsing 或加版本列;实时看板只要最新值,用 Replacing。
- 判断指标可加性:可加走 Summing,不可加走 Aggregating。
- 确认查询模式:明细查询用 Replacing/Collapsing,聚合查询用 Summing/Aggregating。
- 验证写入与合并行为:用
OPTIMIZE ... FINAL验证合并结果,再决定查询是否必须带 FINAL。
| 引擎 | 核心语义 | 典型金融场景 | 关键风险 |
|---|---|---|---|
| ReplacingMergeTree | 主键去重保留最新 | 行情快照、客户最新画像 | 查询未合并会重复 |
| SummingMergeTree | 同维度数值求和 | 成交量、手续费汇总 | 不可加指标会算错 |
| AggregatingMergeTree | 存储聚合中间状态 | UV、分位数、加权均价 | 必须配物化视图 |
| CollapsingMergeTree | Sign 成对抵消撤销 | 撤单、流水冲正 | 写入不成对产生脏数据 |
三、案例场景:订单状态表的选型推演
假设一张订单表,字段为 order_id、status、amount、update_time。业务需要查询“当前有效订单总额”,同时保留撤单审计。若直接用 ReplacingMergeTree,撤单只是把 status 改成 canceled,历史仍在,但无法表达“这笔金额不应计入”。更稳妥的做法是:用 CollapsingMergeTree,撤单时写入一行 Sign=-1 的原始记录和一行 Sign=1 的新状态记录,合并后自动抵消。
CREATE TABLE order_flow (
order_id String,
status String,
amount Decimal(18,2),
update_time DateTime,
Sign Int8
) ENGINE = CollapsingMergeTree(Sign)
ORDER BY (order_id, update_time);
-- 撤单:先抵消旧行,再写入新状态
INSERT INTO order_flow VALUES
('O1001','paid',1000,'2024-05-01 10:00:00',-1),
('O1001','canceled',1000,'2024-05-01 10:05:00',1);
-- 查询有效订单总额
SELECT sum(amount * Sign) FROM order_flow;
如果业务只关心“最新状态”,且能接受最终一致,ReplacingMergeTree 更简单:
CREATE TABLE order_latest (
order_id String,
status String,
amount Decimal(18,2),
update_time DateTime
) ENGINE = ReplacingMergeTree(update_time)
ORDER BY order_id;
SELECT order_id, argMax(status, update_time) AS status
FROM order_latest GROUP BY order_id;
四、误区与复盘:面试中最容易踩的坑
误区一:认为 ReplacingMergeTree 查询自动去重。 去重只在后台 merge 发生,查询时可能看到重复行。正确做法是查询加 FINAL,或用 argMax 聚合。
误区二:用 SummingMergeTree 存均价。 均价不可加,合并后求和会得到错误结果。应改用 AggregatingMergeTree 存 sumState 与 countState,查询时相除。
误区三:CollapsingMergeTree 写入不成对。 撤单必须成对写入,否则 Sign 无法抵消,数据会持续膨胀且结果错误。
面试表达模板:“这个场景的数据会按主键更新,且只需要最新值,所以我选 ReplacingMergeTree,并用版本列控制保留顺序;查询侧我会用 argMax 保证结果正确。如果涉及撤单审计,我会换成 CollapsingMergeTree,并强调写入必须成对。”
复盘时可以用一句话收束:Replacing 管“最新”,Summing 管“可加”,Aggregating 管“复杂聚合”,Collapsing 管“撤销”。 把这句话和具体业务场景绑定,面试官就能确认你不仅记得引擎,更懂得为金融数据的正确性负责。