在使用flink的时候如何保证数据的一致性

错误示范(苦涩干燥版)

Flink 主要靠 Checkpoint 机制保证一致性,支持最多一次、至少一次、精确一次三种语义,要实现端到端一致性的话,source 支持重放、sink 支持幂等或者两阶段提交就可以了。[你说错了吗?并没有, 你回答的好吗? 不润!!!!!!!!!!!!!!!!!!!!!!]

叙事式高分回答版

这个我印象特别深,去年我们做电商实时对账系统的时候,一致性要求卡得特别死 —— 要把用户端的支付订单和微信 / 支付宝的回调流水做实时匹配,差 1 毛钱财务都要追着我们查一下午。最早上线的时候图省事,我们只开了 Flink 默认的 At-Least-Once 语义,结果上线第二周就碰到宿主机硬件故障,作业重启之后对账结果直接差了 2376 块,排查了整整两个小时才定位到是数据重复计算导致的一致性问题,后来我们就把 Flink 全链路一致性的方案彻底优化了一遍:首先是 Flink 内部的一致性保障,我们直接把 Checkpoint 语义改成了 Exactly-Once,它的底层其实是基于 Chandy-Lamport 分布式快照算法实现的:JobManager 会周期性往数据流里插入 barrier 屏障,每个算子收到所有上游分区的 barrier 之后就会做当前状态的快照,我们当时用的是 RocksDB 增量 Checkpoint 把状态持久化到 HDFS,哪怕作业全挂,重启之后直接从最近一次完成的快照恢复状态,内部计算的结果 100% 是准确的。然后是端到端的一致性,我们分了三段做适配:

  1. Source 端用的是 Kafka,Flink 会把消费的 offset 作为状态的一部分存到 Checkpoint 里,重启之后自动从上次提交的 offset 重新拉取数据,既不会丢数也不会多拉数据;
  2. Sink 端我们当时有两个输出:一个是对账结果写 MySQL,我们直接用了 Flink 提供的 JDBC 两阶段提交 Sink,预提交阶段先把数据打到 MySQL 的临时事务里,等整个 Job 的 Checkpoint 全链路都完成之后再正式提交事务,如果中间任何环节失败直接回滚临时事务,不会落脏数据;另一个是对账异常数据要写到 Redis 做报警推送,Redis 不支持事务,我们就做了幂等设计,写入的 key 直接用「订单号 + 第三方流水号 + 对账时间戳」的唯一组合,就算重复写入也只会覆盖同一条数据,不会产生冗余的异常记录。后来我们还加了非对齐 Checkpoint 的配置,就算作业出现短时背压,也不会因为 barrier 对齐超时导致 Checkpoint 失败,进一步保证了一致性的可靠性。这套方案上线快 1 年了,中间碰到过两次机房网络波动、三次作业因资源不足重启,从来没出现过一次数据不一致的问题,对账准确率一直是 100%,财务再也没找过我们麻烦。
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐