4. 资源分配策略:并行度设置、内存配置、CPU与网络资源的动态调整

资源分配这事儿,说白了就是「给多少、怎么给、何时给」。很多团队把并行度设死、内存写死,结果流量一波动,要么资源空转烧钱,要么任务 OOM 挂掉。我这些年踩过的坑,大多跟「静态配置」有关。

这一章,咱们就聊聊怎么让资源分配变得有弹性。核心就三个字:动态调。

4.1 并行度设置:从拍脑袋到按需分配

并行度是实时任务最直接的资源杠杆。设高了浪费 slot,设低了背压爆炸。

我的经验公式:

  • Source 并行度:跟分区数对齐。Kafka 有 12 个分区,并行度就设 12。多了没用,少了拉不满。
  • KeyBy 后的算子:看数据倾斜程度。我曾经遇到一个任务,某个 key 占了 70% 流量,并行度设再高也没用,得先做 rebalance 或 rescale。
  • Sink 并行度:看下游写入能力。HBase 批次写 5000 条最稳,并行度 = 预估 TPS / 5000。

核心原则:并行度不是越大越好。每增加一个并行度,网络 shuffle 和状态后端压力都会上升。我习惯用「每核心每秒处理 5000 条事件」作为基准线,再根据实际压测微调。

4.2 内存配置:Heap 与 Off-Heap 的博弈

内存是实时任务最容易出问题的地方。Flink 的 TM 内存分三块:JVM Heap、Managed Memory、Network Buffer。

内存区域 用途 我常用的比例
JVM Heap 用户代码、状态后端(RocksDB 除外) 40%
Managed Memory 排序、缓存、RocksDB 块缓存 40%
Network Buffer 网络数据交换、反压缓冲区 20%

避坑指南:

  • 我曾经把 Managed Memory 设得太小,导致 RocksDB 频繁刷盘,读写延迟飙升到 200ms。后来调到 40%,延迟降到 20ms。
  • Network Buffer 不足时,背压会直接导致 Checkpoint 超时。我建议至少留 20%,如果网络抖动频繁,可以提到 25%。

注意:不要给 JVM Heap 留太多。GC 停顿是实时任务的大敌。我见过有人给 Heap 分了 80%,结果 Full GC 一次 5 秒,任务直接重启。留 40% 给 Managed Memory,让 RocksDB 自己管理内存,反而更稳。

4.3 CPU 与网络资源的动态调整

静态配置最大的问题,就是扛不住流量毛刺。你想想看,双十一流量是平时的 10 倍,但资源不能按峰值配,否则平时浪费太多。

我的做法:

  1. CPU 弹性:使用 Kubernetes 的 Vertical Pod Autoscaler,根据 CPU 使用率动态调整 request 和 limit。阈值我设 70%,超过就扩容。
  2. 网络带宽:在 Flink 层面开启 auto-watermark-interval 和 backpressure-monitoring。当背压超过 0.8 时,自动降低 source 的消费速率,或者临时增加 sink 并行度。
  3. 动态并行度:Flink 1.15+ 支持 reactive mode,可以根据 slot 数量自动调整并行度。我试过,效果不错,但要注意状态重分布的开销。

小技巧:网络资源调整时,别只盯着带宽。TCP 连接数、连接池大小、序列化方式,这些细节往往才是瓶颈。我曾经把一个 JSON 序列化改成 Protobuf,网络开销直接降了 60%。

4.4 动态调整的落地框架

光有策略不行,得能落地。我画了一张图,把整个动态调整的闭环串起来:

资源动态调整闭环流程图 资源动态调整闭环 监控采集 CPU / 内存 / 背压 / 延迟 指标分析 阈值判断 / 趋势预测 决策引擎 并行度 / 内存 / 网络调整 执行调整 API 调用 / 配置热更新 效果评估 是否达到预期? 反馈闭环 关键指标 CPU < 70% | 背压 < 0.8 延迟 < 100ms | 内存 < 80%

这个闭环的核心是「监控 → 分析 → 决策 → 执行 → 评估」。我一般在决策引擎里加一个冷却期,比如调整后至少等 2 分钟再触发下一次调整,防止震荡。

4.5 实战案例:一次双十一的资源配置

去年双十一,我负责的一个实时风控任务,平时 TPS 是 5 万,峰值预估 50 万。如果按峰值配资源,需要 200 个 slot,平时浪费 75%。

我的方案:

  • 平时:40 个 slot,并行度 8,内存 4GB/TM。
  • 动态策略:当背压超过 0.6 持续 30 秒,自动扩容到 80 个 slot,并行度翻倍,内存升到 8GB/TM。
  • 回缩策略:背压低于 0.2 持续 5 分钟,逐步回缩到 40 个 slot。

结果:峰值时资源刚好够用,平时成本降低了 60%。

关键点:动态调整不是无限弹性。我设了上限 80 个 slot,超过这个数说明架构有问题,得从源头优化,而不是硬扛。

4.6 避坑清单

  • 不要频繁调整并行度:每次调整都会触发状态重分布,频繁操作会导致任务不稳定。我建议调整间隔至少 5 分钟。
  • 内存不要设成整数:比如 4GB,JVM 会分配 4GB + 元空间,容易超限。我习惯设 3.5GB 或 4.2GB,留点余量。
  • 网络 buffer 不要用默认值:默认 64MB 太小,高吞吐场景建议 256MB 起步。
  • 动态调整要加熔断:如果连续 3 次调整后指标反而恶化,立即回滚到上一版配置,并告警。