2. Schema版本管理:版本号策略、向后兼容性、向前兼容性、破坏性变更管理
版本管理这事儿,说白了就是给Schema上个“身份证”。没有版本号,你根本不知道线上跑的是哪一套规则。我见过太多团队因为版本混乱,生产事故一查就是半天——最后发现是消费方还在用老版本的Schema解析新数据。
这一节,我们聊聊版本号怎么定、兼容性怎么判断、破坏性变更怎么管。嗯,都是实战里踩过的坑。
2.1 版本号策略:语义化版本
我个人习惯用语义化版本(SemVer)。格式很简单:MAJOR.MINOR.PATCH。
| 位段 | 含义 | 触发条件 |
|---|---|---|
| MAJOR | 主版本号 | 做了破坏性变更,不兼容旧版本 |
| MINOR | 次版本号 | 新增字段/功能,但向后兼容 |
| PATCH | 修订号 | 修复文档、注释、非功能性调整 |
举个例子:v2.3.1 表示主版本2,次版本3,修订1。每次发布新Schema,必须更新版本号。不能手改,要自动化。
schema_version字段,避免和业务数据混淆。
2.2 向后兼容性
向后兼容,说白了就是新Schema能读旧数据。这是最基础的要求。你想想看,如果升级Schema后,历史数据全废了,那谁还敢升级?
我在项目中遇到过这种情况:某团队给用户表加了个phone字段,但没设默认值。结果老数据反序列化时直接报错——因为新代码要求phone非空。这就是典型的向后不兼容。
向后兼容的几条铁律:
- 只能加字段,不能删字段。删了,老数据就缺字段。
- 新增字段必须有默认值。要么是null,要么是空字符串,要么是0。
- 不能改字段类型。比如int改成string,老数据解析会崩。
- 不能改字段语义。比如原来存手机号,现在存邮箱,消费方会懵。
核心原则:向后兼容 = 老数据在新Schema下能正常反序列化,不丢字段,不报错。
2.3 向前兼容性
向前兼容,很多人不重视。它的意思是老代码能读新数据。你想想,如果消费方没升级,但生产方已经发了新Schema的数据,老代码能不能处理?
我曾经踩过一个坑:某服务升级了Schema,加了几个字段,但消费方没同步升级。结果消费方反序列化时,遇到不认识的新字段直接抛异常。嗯,这就是向前不兼容。
向前兼容的要点:
- 消费方要忽略不认识的新字段。Avro、Protobuf、JSON Schema都支持这种“宽松解析”。
- 不能删除消费方依赖的字段。删了,老代码就找不到数据了。
- 不能改变字段顺序(对于位置敏感的格式,比如Avro)。
2.4 破坏性变更管理
破坏性变更,就是改了之后,新旧版本不兼容。比如删字段、改类型、改语义。这类变更不能偷偷摸摸做,必须走流程。
我一般把破坏性变更分成三类:
| 类型 | 举例 | 影响 |
|---|---|---|
| 删除字段 | 去掉age字段 |
消费方读不到数据 |
| 修改类型 | int改成string |
反序列化失败 |
| 修改约束 | 非空改成必填 | 老数据校验不通过 |
管理破坏性变更,我建议走这几步:
- 提前通知。至少提前一个发布周期,邮件或IM群告知所有消费方。
- 提供迁移窗口。新旧版本同时存在一段时间,让消费方逐步升级。
- 版本号升MAJOR。明确告诉所有人:这是大版本更新。
- 自动化检查。CI/CD里加兼容性检查,发现破坏性变更直接阻断发布。
2.5 兼容性矩阵与决策流程
下面这张图,是我自己总结的兼容性决策流程。每次改Schema,我都会过一遍。
这个流程我用了好几年,基本没出过岔子。核心就两点:先判断是否破坏性,再决定版本号怎么升。最后一定要过CI/CD检查,不能靠人工拍脑袋。
2.6 工具推荐
光靠人盯,迟早出事。我建议用工具自动化:
- Avro:官方提供CompatibilityChecker,支持BACKWARD、FORWARD、FULL等模式。
- Protobuf:用
buf工具做breaking change检测。 - JSON Schema:可以用
ajv配合自定义规则做兼容性校验。 - Schema Registry:Confluent Schema Registry自带兼容性检查,集成CI很方便。
一句话总结:版本号是Schema的身份证,兼容性是Schema的通行证。破坏性变更必须走流程,不能偷偷改。