技术探索与实践解决方案:从“被产品追着跑”到“带着运营飞”
大家好,我是老K,坐标上海,某电商中台团队刚升上来的技术组长(嘘,其实也就比普通开发多背了两个锅)。工作五年,头发少了三分之一,但对云原生、K8s和各种“救火”场景的热情却一点没减。平时除了写代码,最爱参加各种技术沙龙——当然,不是为了学技术(其实是),主要是为了蹭盒饭,顺便看看有没有跳槽机会(bushi)。
上周五晚上十点半,我正瘫在公司楼下那家24小时麦当劳里啃鸡翅(别问,问就是双11压测完的“庆功宴”),突然收到产品经理小李的消息:“老K,咱们那个用户增长看板能不能支持实时数据?运营那边急着要,明天早上站会就要用!”
我当时差点把可乐喷出来——这需求三天前才提,文档都没写全,现在说“明天早上要用”?但没办法,谁让我是技术组长呢?总不能让刚毕业的小张去扛这个雷。
于是,一场关于如何快速构建高可用、低延迟、易运维的实时数据产品的技术探索之旅,就这么开始了。
问题背景:产品要快,运营要准,我们只有48小时
事情得从去年双11说起。当时我们搞了个“裂变拉新”活动,运营同学每天盯着后台数据,发现用户从点击分享到完成注册的链路漏斗断得跟筛子一样。他们想要一个能分钟级更新、支持多维下钻、还能对接BI工具的数据看板。
但现实很骨感:
- 原有的离线数仓T+1,根本满足不了运营“今天投了1000块广告,现在就要看ROI”的诉求;
- 实时计算用的是老掉牙的Storm,集群三天两头挂,运维同事看到我们的工单就绕道走;
- 更惨的是,前端直接调后端API查数据库,高峰期DB CPU直接飙到90%,DBA半夜打电话骂人。
一句话总结:产品要敏捷迭代,运营要精准决策,而我们还在用“石器时代”的架构硬撑。
技术选型:别再盲目追新了,先看业务场景!
说实话,面对“实时数据”这种需求,市面上方案一堆:Flink、Spark Streaming、Kafka Streams、甚至有人推荐用Redis做流处理(笑死)。但作为刚升职的“背锅侠”,我深知:技术选型不是拼谁更潮,而是看谁最稳、最省事、最容易甩锅(划掉)交接。
我们内部开了个紧急会议(其实就是微信群里@所有人),列出了几个核心诉求:
| 维度 | 要求 |
|---|---|
| 延迟 | ≤5分钟(运营忍不了更久) |
| 准确性 | Exactly-once,不能少也不能多 |
| 运维成本 | 最好能跑在现有K8s集群上,别再让运维兄弟哭 |
| 开发效率 | 团队熟悉Java/Scala,别整Python/Rust那些花活 |
| 扩展性 | 后面可能接AB测试、用户画像,得留接口 |
基于这些,我们对比了三个主流方案:
| 方案 | Flink | Spark Streaming | Kafka Streams |
|---|---|---|---|
| 延迟 | 毫秒~秒级 | 秒~分钟级 | 毫秒~秒级 |
| 语义保证 | Exactly-once(成熟) | At-least-once(需额外处理) | Exactly-once(Kafka 0.11+) |
| K8s友好度 | ⭐⭐⭐⭐(官方Operator) | ⭐⭐(需自研调度) | ⭐(需嵌入应用,难独立运维) |
| 学习曲线 | 中等(团队有基础) | 高(RDD/DStream概念多) | 低(但调试困难) |
| 生态整合 | 丰富(Table API, CDC, Iceberg) | 一般 | 弱 |
结论很明显:Flink赢麻了。
虽然Kafka Streams看起来轻量,但它要求把流处理逻辑嵌入业务服务,万一出问题整个服务都得重启——这在我们这种高频发布、天天灰度的环境里简直是灾难。而Spark Streaming的微批模式注定达不到5分钟内更新的要求。
于是,拍板:上Flink + Kafka + K8s。
实践踩坑:你以为的“开箱即用”,其实是“开箱即崩”
理想很丰满,现实嘛……你们懂的。
坑1:K8s上跑Flink,State Backend怎么选?
一开始图省事,用了默认的MemoryStateBackend,结果任务一跑,内存炸了。后来换成FsStateBackend,挂载NFS,结果网络抖动直接导致checkpoint失败,任务自动重启——运营看板数据又断了!
最后咬牙上了RocksDBStateBackend + S3(公司私有云对象存储),配合增量checkpoint,终于稳了。配置如下:
state.backend: rocksdb
state.checkpoints.dir: s3://our-data-lake/checkpoints/
state.savepoints.dir: s3.
execution.checkpointing.interval: 300000 # 5分钟一次
📌 经验:State backend别贪快,稳定性和持久化才是王道。RocksDB虽然CPU高点,但至少不会让你半夜被PagerDuty叫醒。
坑2:Exactly-once?你得自己兜底!
Flink号称Exactly-once,但前提是Source、Sink、中间链路全部支持。我们的数据源是MySQL binlog(通过Debezium),Sink是ClickHouse。
问题来了:ClickHouse官方JDBC不支持事务!这意味着如果Flink在写入中途挂了,可能会重复写。
解决方案:我们在Sink层加了个“幂等写入”逻辑——每条记录带一个event_id,ClickHouse建表时用ReplacingMergeTree引擎,按event_id去重。
CREATE TABLE user_events (
event_id String,
user_id UInt64,
event_time DateTime,
...
) ENGINE = ReplacingMergeTree(event_time)
ORDER BY (user_id, event_id);
虽然牺牲了一点实时性(Merge需要时间),但至少保证了数据不重不漏。
坑3:运营说“这个指标不对”,其实是产品改了口径!
上线第二天,运营总监冲进技术区:“为什么昨天新增用户数比产品给的少了2000?”
我一查,发现产品悄悄改了“有效注册”的定义——原来要完成手机号验证+首单支付,现在只要手机号验证就行。
这时候,数据血缘和指标管理的重要性就体现出来了。我们赶紧接入了Apache Atlas做元数据追踪,并在Flink SQL里把核心指标抽象成视图:
CREATE VIEW valid_signups AS
SELECT user_id, reg_time
FROM raw_events
WHERE event_type = 'phone_verified'
AND reg_time >= '2023-10-01'; -- 产品改口径?改这里就行!
从此以后,产品改需求,只需改SQL,不用动代码。运维和测试也松了口气——毕竟不用每次回归全链路了。
成果与反思:技术不是目的,解决问题才是
经过两周的折腾(包括三个通宵、两次线上回滚、以及请运维兄弟吃了五顿火锅),新系统终于稳定上线。
效果如何?
- 数据延迟:平均2分17秒(远低于5分钟要求)
- 准确率:99.98%(剩下0.02%是产品自己改口径忘了通知我们)
- 运维成本:所有Flink Job通过K8s CRD管理,扩缩容一条命令搞定
- 产品满意度:小李请我喝了杯瑞幸(虽然只点了美式,抠门)
更重要的是,运营同学现在能自己拖拽维度看实时转化率了,再也不用天天追着我们问“数据好了吗”。
写在最后:技术人的成长,是从“实现需求”到“定义问题”
刚工作那会儿,我觉得技术就是写代码、调性能、怼Bug。现在当了组长才明白:真正的技术价值,在于用合理的方案,把模糊的业务语言(比如“快一点”、“准一点”)翻译成可执行、可衡量、可持续的系统。
产品不懂技术细节,运营只关心数字是否可信——而我们要做的,是在两者之间搭一座桥。这座桥不需要金碧辉煌,但必须结实、好走、还能随时加宽。
所以,下次再遇到“明天就要”的需求,别急着砸电脑。深呼吸,泡杯茶(或者续杯瑞幸),然后问清楚:你们到底要解决什么问题?
毕竟,我们不是码农,是问题终结者(虽然工资没涨多少)。
PS:如果你也在上海,对云原生/实时计算感兴趣,欢迎来参加下周六的“魔都Tech Meetup”——我在那儿分享这次实战,现场有披萨!(真的,不是骗你来听广告)

评论 0