Exactly-Once语义:端到端不丢不重的完整链路
三种语义 · 两阶段提交 · 端到端保证 · Kafka事务
📋 面试题
Flink怎么保证Exactly-Once?端到端的Exactly-Once是怎么实现的?
💡 先想30秒,再往下看答案
三种语义
| 语义 | 含义 | Flink支持 |
|---|---|---|
| At-Most-Once | 最多处理一次,可能丢 | 不开Checkpoint |
| At-Least-Once | 至少一次,可能重复 | Checkpoint + 非对齐Barrier |
| Exactly-Once | 恰好一次,不丢不重 | Checkpoint + Barrier对齐 |
面试关键:Flink内部通过Checkpoint+Barrier对齐就能保证Exactly-Once。但端到端的Exactly-Once还需要Source和Sink的配合。
Flink内部的Exactly-Once
DAY5讲过,Checkpoint做Barrier对齐时,所有上游Barrier到齐后才做快照。这保证了State的一致性。
故障恢复时:回退到最近的Checkpoint → Source从保存的offset重新消费 → State恢复到快照时刻 → 重新计算。
这个过程中数据会被重新处理,但因为State也回退了,所以最终结果等价于每条数据只处理了一次。
端到端Exactly-Once
| 环节 | 要求 | 实现方式 |
|---|---|---|
| Source | 可重放 | Kafka(可回退offset) |
| Flink内部 | 一致性快照 | Checkpoint + Barrier对齐 |
| Sink | 幂等写入或事务写入 | 两阶段提交 / 幂等写 |
Sink是最难的一环。如果Sink在Checkpoint之后、下次Checkpoint之前写了数据然后任务挂了,恢复后会重复写。
两阶段提交(2PC)
阶段一:预提交(Pre-Commit)
Checkpoint触发时,Sink开启事务,数据写入但不可见
阶段二:正式提交(Commit)
Checkpoint成功后,Sink提交事务,数据变为可见
如果Checkpoint失败 → 事务回滚,数据作废
Kafka Sink的两阶段提交
// Kafka Sink开启Exactly-Once
KafkaSink<String> sink = KafkaSink.<String>builder()
.setBootstrapServers("kafka:9092")
.setDeliverGuarantee(DeliveryGuarantee.EXACTLY_ONCE)
.setTransactionalIdPrefix("flink-tx")
.setRecordSerializer(...)
.build();
// 注意:Kafka消费端也要设置 isolation.level=read_committed
// 否则消费端能看到未提交的数据,Exactly-Once就失效了
我在做广告归因时用了Kafka→Flink→Kafka链路的Exactly-Once。上线后发现下游消费者没设read_committed,导致消费到了未提交的数据。改了消费端配置后才真正端到端Exactly-Once。
幂等写入(更实用的方案)
很多Sink不支持事务(比如Redis、HBase、大部分OLAP引擎),这时用幂等写入:
幂等 = 同一条数据写多次,结果和写一次一样
实现方式:
- 用唯一主键覆盖写(Doris/StarRocks的主键模型)
- UPSERT语义(有则更新,无则插入)
- 写入前去重(用Redis/布隆过滤器判断是否已写过)
我们写Doris用的就是幂等方案。Doris的Unique Key模型天然支持UPSERT,相同主键自动覆盖。比两阶段提交简单得多,也够用。
面试加分提示
💡 讲清楚内部和端到端的区别:
「Flink内部通过Checkpoint + Barrier对齐保证Exactly-Once。但端到端还需要Source可重放、Sink事务写或幂等写。
Kafka Sink通过两阶段提交实现事务写,但下游消费端必须设read_committed。实际项目中如果Sink支持UPSERT(比如Doris),用幂等写更简单可靠。」
📌 总结
| 记忆点 | 内容 |
|---|---|
| 三种语义 | At-Most-Once / At-Least-Once / Exactly-Once |
| 内部保证 | Checkpoint + Barrier对齐 |
| 端到端 | Source可重放 + 内部Checkpoint + Sink事务或幂等 |
| 两阶段提交 | 预提交(不可见) → Checkpoint成功 → 正式提交 |
| 幂等方案 | UPSERT/主键覆盖,比2PC更简单实用 |
▶ 下一篇预告:DAY 7 反压排查
Wood聊大数据 | 9年大数据老兵,聊技术、聊职场、聊成长
小红书正文
Exactly-Once是Flink面试高频题,但很多人只会说「Checkpoint保证」就没了。这最多60分。
关键区别:Flink内部Exactly-Once靠Checkpoint+Barrier对齐。但端到端还需要Source可重放+Sink事务写或幂等写。
Kafka Sink用两阶段提交:Checkpoint触发时开事务写但不可见,Checkpoint成功后提交事务。注意下游消费端要设read_committed,否则Exactly-Once失效。
实际项目中更实用的是幂等写:Doris/StarRocks的主键模型天然UPSERT,相同主键自动覆盖。比两阶段提交简单得多。