结合 Kafka + MV + ReplacingMergeTree 构建实时去重管道

Kafka + 物化视图(MV) + ReplacingMergeTree 结合使用,是构建 ClickHouse 实时去重数据管道 的经典架构。它广泛应用于日志去重、用户行为分析、状态更新、CDC(变更数据捕获)等场景,能够实现 高吞吐、低延迟、最终一致性去重

本篇将带你一步步构建一个完整的 实时去重管道,从 Kafka 消费消息,通过物化视图写入去重表,最终提供高效查询。


🎯 一、目标架构

[Kafka] 
   │
   ↓ (消息流)
[ClickHouse Kafka Engine 表] → 消费消息
   │
   ↓
[物化视图 MV] → 转换 & 写入
   │
   ↓
[ReplacingMergeTree 表] → 按主键去重,保留最新
   │
   ↓
[查询层] → 使用 argMax 获取最终状态

✅ 实现效果:

  • 实时摄入 Kafka 数据
  • 自动去重(相同主键保留最新)
  • 支持高效聚合查询

🧰 二、环境准备

组件要求
ClickHouse≥ v20.8(推荐 v22+)
Kafka≥ v1.0(支持 JSON/Protobuf)
网络ClickHouse 能访问 Kafka 集群
数据格式推荐 JSONEachRowProtobuf

📦 三、步骤 1:创建 Kafka 引擎表(消息接口)

这个表不存储数据,仅作为 Kafka 消息的“入口”。

CREATE TABLE kafka_user_events (
    user_id UInt32,
    event_type String,
    page String,
    device_id String,
    event_time DateTime,
    update_version UInt64  -- 用于去重版本(如 Kafka offset 或业务版本号)
) ENGINE = Kafka
SETTINGS 
    kafka_broker_list = 'kafka1:9092,kafka2:9092',
    kafka_topic_list = 'user-events-topic',
    kafka_group_name = 'clickhouse-consumer-group',
    kafka_format = 'JSONEachRow',  -- 每行一个 JSON 对象
    kafka_num_consumers = 4;       -- 每个表 4 个消费者,提升吞吐

✅ 示例 Kafka 消息:

{"user_id":1001,"event_type":"pageview","page":"/home","device_id":"dev123","event_time":"2024-04-01 10:00:00","update_version":1}

🧩 四、步骤 2:创建去重目标表(ReplacingMergeTree)

该表用于存储去重后的数据。

CREATE TABLE user_events_dedup (
    user_id UInt32,
    event_type String,
    page String,
    device_id String,
    event_time DateTime,
    update_version UInt64
) ENGINE = ReplacingMergeTree(update_version)  -- 以 update_version 为版本列
PARTITION BY toYYYYMM(event_time)
ORDER BY (user_id, event_time);  -- 按 user_id 去重,按时间排序

✅ 关键点:

  • ReplacingMergeTree(update_version):合并时保留 update_version 最大的记录
  • ORDER BY (user_id, ...)user_id 是去重主键
  • PARTITION BY:按月分区,便于管理

🚀 五、步骤 3:创建物化视图(自动写入管道)

物化视图监听 kafka_user_events,自动将数据写入 user_events_dedup

CREATE MATERIALIZED VIEW mv_kafka_to_dedup
TO user_events_dedup
AS SELECT
    user_id,
    event_type,
    page,
    device_id,
    event_time,
    update_version
FROM kafka_user_events;

✅ 效果:

  • Kafka 中每新增一条消息,物化视图自动触发
  • 数据写入 user_events_dedup,等待后台合并去重

🔄 六、工作流程详解

1. 数据流入

Kafka 生产者
     │
     ↓
user-events-topic
     │
     ↓
kafka_user_events(Kafka Engine)
     │
     ↓
物化视图 mv_kafka_to_dedup
     │
     ↓
user_events_dedup(ReplacingMergeTree)

2. 去重机制

假设 Kafka 中有以下消息:

user_idevent_typeupdate_version
1001login1
1001login1
1001logout2

后台 Merge 任务执行后:

  • 相同 (user_id, event_time) 的记录被合并
  • 保留 update_version = 2logout 记录

📊 七、查询去重结果(推荐方式)

❌ 避免使用 FINAL

-- 性能极差,全表扫描,不推荐
SELECT * FROM user_events_dedup FINAL WHERE user_id = 1001;

✅ 推荐:GROUP BY + argMax

SELECT
    user_id,
    argMax(event_type, update_version) AS latest_event,
    argMax(page, update_version) AS latest_page,
    argMax(device_id, update_version) AS device_id,
    max(event_time) AS last_active
FROM user_events_dedup
WHERE event_time >= '2024-04-01'
GROUP BY user_id
HAVING latest_event != 'logout'  -- 可继续过滤
ORDER BY last_active DESC
LIMIT 100;

✅ 优势:

  • 利用 ORDER BY 和分区裁剪
  • 并行执行,性能高
  • 可扩展为多维度聚合

📈 八、性能优化建议

优化项建议
Kafka 消费kafka_num_consumers = CPU 核数
写入批次Kafka 消息批量发送(>1000 条/批)
物化视图逻辑尽量简单,避免复杂 JOIN
分区策略按时间分区,便于 TTL 和删除
TTL自动清理过期数据
TTL event_time + INTERVAL 90 DAY

| 监控 | 监控 system.kafka_consumers, system.parts, query_log |


⚠️ 九、常见问题与解决方案

问题原因解决方案
数据未去重合并未触发手动 OPTIMIZE TABLE user_events_dedup
Kafka 消费延迟消费者慢增加 kafka_num_consumers
物化视图卡住源表无数据或错误检查 system.tables 和日志
argMax 结果不准update_version 不单调确保版本号递增
内存不足查询大表增加 max_memory_usage 或分页查询

🛠️ 十、扩展:支持多主键去重

如果去重键是多个字段(如 user_id + session_id):

ORDER BY (user_id, session_id, event_time)

查询时:

SELECT
    user_id,
    session_id,
    argMax(page, update_version) AS last_page
FROM user_events_dedup
GROUP BY user_id, session_id;

🎯 十一、总结:Kafka + MV + ReplacingMergeTree 的核心价值

组件职责优势
Kafka Engine实时消费消息解耦生产与消费,高吞吐
Materialized View自动写入管道无需外部 ETL,实时处理
ReplacingMergeTree最终一致性去重存储高效,查询快

🎯 这套组合是 ClickHouse 构建“实时数仓”的“黄金三角”

  • 实时性:秒级延迟
  • 可靠性:Kafka 保证不丢
  • 去重:最终一致性
  • 可扩展:支持 PB 级数据
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值