【背景】
做了 8 年中间件研发,从 Kafka 0.11 一路摸到 3.x、Redis 从 3.2 到 7.2、MySQL 从 5.6 到 8.0,每个大版本升级都经历过。最近帮两个团队做升级方案评审,发现两边的做法截然相反,而且各自都翻过车:
- 团队 A(金融核心链路):坚持"一次升级、一次割接",Kafka 2.8 → 3.3 用了 3 周准备,割接窗口 4 小时,结果 broker 升级后 consumer 的
inter.broker.protocol.version和 producer 的acks=all组合触发了一个 3.x 才有的 partition leader 选举行为变化,下游对账延迟 40 分钟才被发现。 - 团队 B(电商中台):追求"零停机、灰度 6 个月",Redis 5 和 Redis 7 并存了整整 8 个月,期间双写 + 一致性校验脚本维护成本极高,最后因为 5.x 的某个内存泄漏 bug 被迫提前收敛,灰度白做了。
两种做法都有道理,也都有代价。但社区里关于"怎么升"的讨论很多(兼容性矩阵、feature flag、回滚预案),关于**"升到什么程度算完、什么时候该收手"的决策框架**,我确实没看到有人系统梳理过。
【常见分歧 / 几种做法】
- 一把梭(Big Bang):所有 broker / 节点在一个维护窗口内完成升级。优点是窗口短、不存在双版本并存期;缺点是回滚代价大(数据可能已经用新版本格式写过),且窗口内故障影响面大。
- 滚动升级(Rolling):逐节点升级,利用 broker 间的副本机制或 Redis 的 replication 保证可用性。Kafka 的 inter-broker protocol version 机制就是为这个设计的。但滚动升级的"中间态"窗口可能持续数小时到数天,期间新旧版本混跑,某些 edge case 只在中间态出现。
- 双版本并存 + 灰度切流:新旧版本同时跑,流量按比例或按业务维度切。优点是风险最低、随时可回退;缺点是运维成本翻倍(两套监控、两套告警、数据一致性校验),且"并存期"可能拖成"长期状态"。
- 只升 client、不升 server:比如 Kafka client 3.x 连 broker 2.8,或 Redis client 连旧版 server。短期省事,但长期会积累"client 比 server 新"的技术债,而且某些新特性(如 Kafka 3.x 的 KIP-848)必须 server 端支持才能用。
【我的倾向与理由】
我的判断框架是按"数据格式是否向后兼容"来分级,而不是按"版本号差了几个"来分:
- 格式兼容(如 Kafka 2.8→3.x,log 格式不变;Redis 5→7,RDB/AOF 格式兼容):滚动升级即可,不存在真正的"中间态风险",双版本并存只是运维冗余,收益有限。
- 格式不兼容或行为变更(如 MySQL 5.7→8.0 的认证插件变更、字符集默认值变化;Kafka 3.x 移除 ZooKeeper 的 KRaft 模式):必须双版本并存 + 灰度切流,且明确设定并存期上限(我们内部定的是 30 天,超期必须收敛),否则并存期会变成"事实上的两个生产集群"。
踩过的最大的坑:Kafka 2.x → 3.x 升级时,log.retention.bytes 的语义在 KRaft 模式下和 ZK 模式下有细微差异(KRaft 下是 per-partition,ZK 下是 per-broker aggregate),我们迁移后没仔细核对,导致一个 4000 分区的 topic 实际保留量比预期多了 3 倍,磁盘在 2 天后打满。
【想听大家聊的】
- 你们团队的中间件升级,是"一把梭"还是"灰度并存"?决策依据是什么——是 SRE 定的规范,还是每次 case by case?
- 有没有"并存期拖成常态"的经历?最后怎么收的场?
- 对于格式兼容但行为有细微差异的升级(比如 Kafka 的 partition 选举策略、Redis 的 eviction 策略变更),你们在升级前怎么验证"行为差异不会影响业务"?是靠读源码、跑集成测试,还是靠生产灰度"撞出来"?
- 你们有没有一个"升级完成"的明确定义(DoD)?比如:所有 client 都升了?监控告警都切到新版本的 metrics 了?回滚预案演练过了?还是只是"broker 版本号变了就算完"?
欢迎拿自己踩过的坑来拍砖,尤其是"升级后第 3 天才发现的问题"——这种最值钱。
0赞
0收藏
0楼层