4. Schema Registry 核心概念:Subject、Schema ID、兼容性类型

好,咱们进入 Schema Registry 最核心的几个概念。说实话,很多初学者一开始就被 Subject、Schema ID、兼容性这些术语搞晕了。我当年第一次接触时也差不多——文档翻了好几遍,总觉得隔着一层纱。后来在项目里真正踩过坑,才把这些东西串起来。

今天咱们就把它们一个一个拆开,讲明白。

4.1 Subject:数据的“身份证”

先问一个问题:Kafka 里一条消息,怎么知道该用哪个 Schema 来校验?

答案就是 Subject。

Subject 说白了就是一个名字,用来关联一组 Schema 版本。在 Schema Registry 里,每个 Subject 下面可以注册多个版本的 Schema。你发消息时带上 Subject 名称,Registry 就知道该用哪个 Schema 来校验。

我习惯把 Subject 理解成“数据的身份证号”。它唯一标识了一类数据。比如订单事件,你可以用 order-value 作为 Subject,所有订单的 Avro Schema 都注册在这个 Subject 下。

关键点: Subject 的命名规则通常是 <topic-name>-<type>,其中 type 可以是 value 或 key。比如 orders-value、orders-key。

嗯,这里要注意:同一个 Topic 的 Key 和 Value 要用不同的 Subject。为什么呢?因为它们的 Schema 结构完全不同。Key 通常只包含业务主键,Value 才是完整的业务数据。混在一起会出大问题。

4.2 Schema ID:版本号的“数字指纹”

有了 Subject,那怎么快速找到某个 Schema 呢?

靠 Schema ID。它是 Schema Registry 为每个注册的 Schema 自动分配的唯一整数 ID。注意,是全局唯一的,不是 Subject 内唯一。

我举个例子:

  • Subject A 注册了 Schema v1,Registry 分配 ID = 1
  • Subject B 注册了 Schema v1,Registry 分配 ID = 2
  • Subject A 注册了 Schema v2,Registry 分配 ID = 3

你看,ID 是递增的,跟 Subject 无关。这样做的好处是:消息里只需要带一个 4 字节的 ID,就能唯一确定 Schema。相比把整个 Schema 塞进消息,这能省下大量带宽。

小技巧: 我在项目中看到过有人把 Schema ID 和版本号搞混。记住:版本号是 Subject 内的,从 1 开始递增;Schema ID 是全局的,整个 Registry 唯一。两者不是一回事。

4.3 兼容性类型:Schema 演进的“交通规则”

好了,现在有了 Subject 和 Schema ID,数据能正常序列化反序列化了。但问题来了:业务总会变,Schema 也会变。今天加个字段,明天删个字段,后天改个类型。怎么保证新老版本能和平共处?

这就是 兼容性类型 要解决的问题。

Schema Registry 支持四种兼容性类型。我直接上表格,对比着看更清楚:

兼容性类型 规则 典型场景
BACKWARD 新 Schema 能读旧 Schema 写的数据 消费者先升级,生产者后升级
FORWARD 旧 Schema 能读新 Schema 写的数据 生产者先升级,消费者后升级
FULL 同时满足 BACKWARD 和 FORWARD 生产者和消费者同时升级,但不确定顺序
NONE 不检查兼容性,随便改 开发环境、快速原型

我一个个说。

4.3.1 BACKWARD 兼容性

这是最常用的类型。规则是:新 Schema 必须能读取旧 Schema 写入的数据。

怎么做到?说白了就是:新 Schema 只能添加有默认值的字段,或者删除没有默认值的字段。这样旧数据反序列化时,新 Schema 能补上默认值,不会报错。

我在项目中遇到过一个问题:有个团队把 BACKWARD 理解反了,以为新 Schema 能写、旧 Schema 能读就行。结果消费者先升级后,读不到生产者旧版本写的数据,线上直接报错。嗯,那场面……

避坑指南: 我曾经因为忘记给新增字段设置默认值,导致 BACKWARD 兼容性检查失败。记住:新增字段必须要有 default 值,否则 Registry 会拒绝注册。

4.3.2 FORWARD 兼容性

FORWARD 正好反过来:旧 Schema 必须能读取新 Schema 写入的数据。

这通常用在生产者先升级的场景。比如你改了数据格式,但消费者还没升级,旧消费者还得能读新数据。

实现方式:新 Schema 只能删除字段,不能新增。因为旧 Schema 不认识新字段,遇到未知字段直接忽略就行。

4.3.3 FULL 兼容性

FULL 就是 BACKWARD + FORWARD 的合体。新 Schema 既能读旧数据,旧 Schema 也能读新数据。

说白了,你只能做两件事:

  • 添加有默认值的字段
  • 删除有默认值的字段

其他操作都不行。这要求最严格,但也是最安全的。我一般建议生产环境用 FULL,除非你有明确的升级顺序控制。

4.3.4 NONE 兼容性

这个最简单——不检查。你想怎么改就怎么改。但后果自负。

我个人的习惯是:开发环境用 NONE,方便快速迭代。但一旦上了测试环境,立刻切到 BACKWARD 或 FULL。否则线上出问题,排查起来很痛苦。

4.4 核心概念关系图

说了这么多,咱们用一张图把这三个概念串起来:

Schema Registry 核心概念关系图 Subject Schema ID 兼容性类型 BACKWARD FORWARD FULL NONE 包含多个版本 版本间检查 💡 说明:Subject 是 Schema 的逻辑分组,Schema ID 是全局唯一标识, 兼容性类型控制 Schema 版本间的演进规则。三者共同保障数据一致性。

4.5 实战中的选择建议

最后,我根据实际经验给点建议:

  • 生产环境:优先用 FULL 或 BACKWARD。如果你能控制升级顺序,BACKWARD 就够了;如果不能,直接上 FULL。
  • 测试环境:用 BACKWARD,模拟生产行为。
  • 开发环境:用 NONE,别让兼容性检查拖慢你的迭代速度。
  • 跨团队协作:一定要统一兼容性策略。我见过两个团队一个用 BACKWARD、一个用 FORWARD,结果 Schema 注册时互相冲突,排查了半天。
个人习惯: 我一般会在 CI/CD 流水线里加一步 Schema 兼容性检查。这样代码合并前就能发现问题,而不是等到上线才报错。

好了,Subject、Schema ID、兼容性类型这三个核心概念,咱们就讲到这里。记住它们的关系:Subject 是门牌号,Schema ID 是身份证号,兼容性类型是交通规则。三者配合,才能让数据在演进中保持一致性。