获课:xingkeit.top/6591/
千万级 TPS:Flink 消费 Kafka 写入 ClickHouse 实战——一个大数据工程师的“极限挑战”手记
如果用一句话总结这段经历,那就是:“千万级 TPS 不是技术神话,是每一毫秒都被榨干的现实。” 这不是一篇纸上谈兵的架构设计,而是我在经历了无数次 OOM、背压、写入超时、以及凌晨三点盯着监控面板心跳加速之后,沉淀下来的真实体感与血泪教训。
一、从“理论上可行”到“线上崩了”
接到这个任务时,我的第一反应是“理论上可行”。Flink 号称百万级吞吐,ClickHouse 号称每秒每节点百万行写入,Kafka 更是以高吞吐著称。三者加在一起,千万级 TPS 似乎只是调调参数的事。
天真了。
第一次压测,流量刚冲到 300 万 TPS,Flink 任务就直接 OOM 了。第二次,调整了内存,背压飙到 0.8,数据开始积压。第三次,ClickHouse 写入超时,连接池被打满,Kafka 消费 Lag 以肉眼可见的速度飙升。
那天晚上我盯着监控面板,心里只有一个念头:“纸上得来终觉浅,绝知此事要躬行。” 千万级 TPS 不是数学题,是系统工程,每一层都有它自己的脾气。
二、Flink:调参如调琴
Flink 消费 Kafka 的第一道坎,是“并行度与分区的匹配”。
Kafka 分区数 120,Flink 并行度 120,听起来完美对齐。但问题是,数据的 Key 分布并不均匀。有些分区数据量是别的分区的 5 倍,导致对应子任务处理不过来,其他子任务却在摸鱼。背压就是这样产生的——不是整体算力不够,而是负载不均衡。
后来我换了一个思路:不追求一一对齐,而是让 Flink 的并行度大于分区数,用轮询方式重新分配数据。 这引入了额外的 shuffle 开销,但换来了更均衡的负载。在千万级 TPS 场景下,均衡比节省那点网络开销重要得多。
另一个让我刻骨铭心的是“状态后端”的选择。最开始用的 RocksDB,心想“磁盘存储不怕 OOM”。结果吞吐量死活上不去,后来发现瓶颈在 RocksDB 的写放大。切换到 Heap State Backend,吞吐量直接翻倍。代价是 Checkpoint 变得巨大,但权衡之后,我选择了牺牲故障恢复速度,换日常处理吞吐——在极限场景下,你必须做出取舍,不可能什么都想要。
三、Kafka:消费者组的“隐秘角落”
Flink 消费 Kafka 时,我一度以为 Kafka 端不会有问题。毕竟 Kafka 号称百万级吞吐,我们才千万级,分摊到 120 个分区,每个分区不到 10 万 TPS,小意思。
但问题出在“消费者 Rebalance”上。
压测过程中,Flink 任务重启,触发了消费者组的 Rebalance。这个过程持续了将近 40 秒,期间整个任务停止消费。40 秒对于千万级 TPS 意味着什么?意味着 4 亿条数据的积压。恢复之后,Flink 要花十几分钟才能消化完积压,这期间下游 ClickHouse 写入压力爆表。
我后来学到的是:在千万级 TPS 场景下,任务重启是奢侈品。 我们做了两件事:一是优化 Flink 任务的稳定性,尽量规避重启;二是配置了 Kafka 的 max.poll.records,控制单次拉取的数据量,减少 Rebalance 时的“停摆时间”。同时,把 session.timeout.ms 调大,给 Flink 任务更长的“喘息时间”,避免被 Kafka 误判为挂掉。
四、ClickHouse:写入的“木桶效应”
千万级 TPS 写入 ClickHouse,最大的挑战不是“能不能写进去”,而是“能不能一直写进去”。
ClickHouse 的写入性能很强,但它有个特点:每批写入都会生成一个 Part,过多的 Part 会导致 Merge 压力剧增。 如果 Flink 以微批方式写入,批次太小,Part 数量爆炸,ClickHouse 的 Merge 线程就被打满,写入延迟急剧上升。
我花了很长时间才找到一个平衡点:批次大小 10 万行,批次间隔 1 秒,两者先到先发。 这个组合让 Part 生成速度控制在 ClickHouse 的承受范围内,同时保证了数据的近实时性。
另一个坑是“写入连接数”。默认配置下,ClickHouse 的 max_connections 是 1024。Flink 并行度 120,每个并行度开一个连接,似乎绰绰有余。但压测时发现,连接池在高峰期会反复创建销毁,导致大量 TIME_WAIT 状态的连接积压。解决方案是在 Flink 侧实现连接池复用,而不是每个批次都新建连接——这个优化在千万级 TPS 下,单机就能省下几百 MB 的内存和大量的 CPU 开销。
五、监控:看不见就无法优化
在千万级 TPS 的极限场景下,有一件事比调优更重要:看得到问题在哪。
我们花了很多精力建设监控体系。Flink 侧,背压指标、Checkpoint 耗时、各算子处理延迟,全部打到 Prometheus。Kafka 侧,消费者 Lag、分区数据倾斜、Rebalance 次数,实时监控。ClickHouse 侧,写入吞吐、Part 数量、Merge 队列长度,一个不落。
有一次凌晨三点,告警响了:写入延迟突增。打开监控一看,ClickHouse 的 Merge 队列堆积了 2 万多个 Part。回溯发现,是 Flink 的某个子任务因为 GC 停顿,批次发送间隔变长,导致数据积压后一次性写入大批数据,ClickHouse 来不及 Merge,写入延迟飙升。
如果没有监控,这个问题可能要等到第二天上班才能发现。而有了实时监控,我 10 分钟内就定位到问题,手动触发了一次 Merge,恢复了服务。
六、写在最后:敬畏每一条数据
回顾这段经历,我最深的体悟是:千万级 TPS 不是靠“调优”调出来的,是靠“设计”设计出来的。
你需要从一开始就考虑数据分布的均衡性,考虑故障恢复时的积压消化能力,考虑每一层组件在极限情况下的行为特征。任何一个环节的短板,都会成为整个链路的瓶颈。
更重要的是,你要学会与“不完美”共存。在千万级 TPS 下,你不可能做到绝对的 exactly-once,不可能做到零延迟,不可能做到永远不出故障。你需要做的是:让系统在极限情况下依然可控,故障时能快速恢复,恢复后能快速追平。
有人说,大数据工程师的工作就是“让数据流动起来”。但在千万级 TPS 的极限场景下,这句话应该改成:“让数据稳定、高效、可观测地流动起来。”
每一毫秒的延迟,每一次 GC 停顿,每一个参数配置,都可能是压垮骆驼的最后一根稻草。而我们要做的,就是在这根稻草落下之前,把整座桥修得更稳一些。
这大概就是大数据工程最迷人的地方——在极限中寻找平衡,在复杂中建立秩序。
本站不存储任何实质资源,该帖为网盘用户发布的网盘链接介绍帖,本文内所有链接指向的云盘网盘资源,其版权归版权方所有!其实际管理权为帖子发布者所有,本站无法操作相关资源。如您认为本站任何介绍帖侵犯了您的合法版权,请发送邮件
[email protected] 进行投诉,我们将在确认本文链接指向的资源存在侵权后,立即删除相关介绍帖子!
暂无评论