Kafka数据去重
前言
Kafka 数据重复可能发生在 生产(Producer)、传输(Broker)、消费(Consumer)等多个环节。
一、Kafka 数据重复的原因?
1.Producer 端
- 未开启幂等性(Idempotence) → 可能导致 消息重复发送。
- ack=1 或 ack=0 → 生产者收到确认前可能重试,造成数据重复。
- 网络异常 / 超时 → 生产者认为消息未发送成功,重新发送。
2. Broker 端
- 副本同步失败 → Leader 切换后,可能导致 消息重复。
- 事务未正确提交 → Producer 事务回滚或重试,可能导致重复提交。
3. Consumer 端
- 手动提交 Offset 失败 → 可能导致 重复消费。
- Consumer 端宕机重启 → 消费进度回滚,可能导致 消息重复处理。
二、解决方案
1.Kafka 端:幂等性 + 事务 + ack=all
- 生产者端:开启幂等性(Idempotence)
Kafka Producer 默认不会去重,需要手动开启幂等性:
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("acks", "all"); // 确保所有副本写入,避免数据丢失
props.put("enable.idempotence", "true"); // 开启幂等性
props.put("retries", Integer.MAX_VALUE); // 无限重试
-
幂等性(Idempotence):Kafka 生产者会为每个消息生成 唯一序列号,Broker 端会去重 相同 Producer ID + 相同序列号 的消息。
-
事务 + 幂等性:防止跨分区重复 如果 Producer 需要跨分区事务,单纯的幂等性不足,需要Kafka 事务:
props.put("transactional.id", "my-transactional-producer"); // 事务 ID
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
producer.initTransactions();
- 事务写入
try {
producer.beginTransaction();
producer.send(new ProducerRecord<>("topic1", "key1", "value1"));
producer.send(new ProducerRecord<>("topic2", "key2", "value2"));
producer.commitTransaction(); // 原子提交
} catch (Exception e) {
producer.abortTransaction(); // 事务回滚
}
✅ 作用:防止部分成功、部分失败的情况,保证 Kafka Exactly Once 语义。
2.下游去重
如果 Kafka 侧仍有少量重复数据,下一层(Spark Streaming、Redis、Hive)可进行去重。
1️⃣ Spark Streaming 去重
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.functions._
val df = spark.readStream.format("kafka").option("subscribe", "my-topic").load()
// 按照 id 进行去重,取最早的数据
val dedupedDF = df.withColumn("rank", row_number()
.over(Window.partitionBy("id").orderBy("timestamp")))
.filter(col("rank") === 1)
// 输出去重后的数据
dedupedDF.writeStream.format("console").start()
✅ 按 id 分组,取最新或最早的一条数据,防止重复。
2️⃣ Redis 去重
Kafka 消费时,可以将数据 ID 存入 Redis,并判断是否已存在:
if (!redis.sismember("processed_ids", messageId)) {
redis.sadd("processed_ids", messageId);
processMessage(message);
}
✅ 作用:利用 Redis Set 结构,保证相同 ID 数据不会被重复处理。
该处使用的url网络请求的数据。
3️⃣ Hive 数据仓库去重
在 DWD(Data Warehouse Detail)层去重,确保进入 DWS 层数据无重复:
SELECT id, data
FROM (
SELECT id, data, row_number() OVER (PARTITION BY id ORDER BY timestamp DESC) AS rank
FROM dwd_table
) t
WHERE rank = 1;
✅ 作用:按 id 分组,每个 id 只取最新一条,去除重复数据。
3. Kafka 数据去重方案对比
方案 | 使用阶段 | 作用 | 使用场景 | 缺点 |
---|---|---|---|---|
Kafka 幂等性 | Producer 去除重复 | 发送的消息 | 日志采集、数据流 | 仅限于Producer端 |
Kafka 事务 | Producer + Broker | 跨分区Exactly Once | 订单、金融数据 | 性能开销较大 |
Spark Streaming 去重 | Consumer | 基于窗口去重 | 实时分析 | 需要额外计算资源 |
Redis去重 | Consumer | 存储 ID过滤重复 | 低延迟业务 | 占用 Redis 内存 |
Hive DWD 层去重 | 数据仓库 | 最终去重 | 离线分析 | 适用于 T+1 数据 |
4. 总结
✅ Kafka Producer
开启幂等性 (enable.idempotence=true),避免 Producer 端重复。
设置 acks=all,确保消息可靠写入。
使用事务 (transactional.id),防止跨分区重复。
✅ Kafka Consumer
设置 isolation.level=read_committed,避免读取未提交事务数据。
手动提交 Offset,确保处理完数据后再提交,防止重复消费。
✅ 下游去重
Spark Streaming 开窗去重。
Redis 记录已消费 ID。
Hive 数据仓库去重。