3. Schema基础概念
在行情数据系统中,Schema 不是一份“可选的文档”,而是数据生产方与消费方之间的数据契约。它规定了每条行情记录有哪些字段、字段的类型与精度、哪些字段必填、字段的语义边界,以及数据在序列化与反序列化时应遵循的规则。理解 Schema 的定义、结构与元数据,是后续处理 Schema 演进与兼容问题的前提。
一、Schema 的定义与结构
Schema 是对数据结构的形式化描述。以行情快照为例,一条记录通常包含标的代码、交易所、时间戳、买卖盘口、最新价、成交量等字段。Schema 需要回答三个层面的问题:结构层(有哪些字段、嵌套关系如何)、类型层(每个字段是整型、浮点、字符串还是枚举)、约束层(取值范围、精度、是否可空、默认值)。
一个典型的行情 Schema 结构可以抽象为:
MarketTick
├── symbol: string (required)
├── exchange: enum {SSE, SZSE, HKEX} (required)
├── ts: int64 (epoch millis, required)
├── bids: array<BidAsk> (max 10)
│ ├── price: decimal(18,4)
│ └── volume: int64
├── asks: array<BidAsk> (max 10)
└── last: decimal(18,4) (nullable)
可以看到,Schema 不只是字段清单,还携带了嵌套、数组容量、精度和可空性等约束。这些约束直接决定了序列化后的字节布局,也决定了上下游系统能否正确解析。
二、元数据与数据契约
Schema 的元数据(Metadata)是描述“描述本身”的信息,常见包括:Schema 版本号、命名空间、作者、创建时间、兼容性策略(如 BACKWARD、FORWARD、FULL)、字段的默认值与别名。元数据让 Schema 具备可治理性——没有版本号和兼容策略的 Schema,在演进时几乎必然引发线上事故。
在行情场景中,契约的破坏往往来自看似无害的改动:把 price 从 float 改为 double、删除一个盘口字段、把必填改为可空。这些改动如果没有元数据中的兼容策略约束,消费方会在反序列化时直接抛异常或读到错误值。
三、主流模式语言概述
不同模式语言在表达力、序列化效率、演进能力上各有取舍。行情系统常见的选择如下表:
| 模式语言 | 表达形式 | 序列化 | 演进能力 | 行情场景适用性 |
|---|---|---|---|---|
| JSON Schema | JSON 文本 | 文本,体积大 | 弱,靠约定 | 配置、REST 接口、调试 |
| Avro | JSON 定义 + 二进制 | 紧凑二进制 | 强,支持读写 Schema 解析 | Kafka 行情管道、批处理 |
| Protobuf | .proto IDL | 紧凑二进制 | 强,字段编号 + 保留位 | 低延迟行情分发、gRPC |
| Thrift | .thrift IDL | 二进制/压缩 | 中,需版本管理 | 跨语言服务、内部 RPC |
JSON Schema 胜在可读与通用,但缺乏强制的二进制契约,行情高频场景下体积和解析开销偏大。Avro 的核心优势是读写 Schema 分离:写入时用写 Schema,读取时用读 Schema,二者通过字段名和默认值做解析,天然支持字段增删。Protobuf 通过字段编号(tag)保证兼容,删除字段时使用 reserved 防止编号复用,适合对延迟敏感的行情分发。Thrift 与 Protobuf 类似,但演进规则更依赖团队纪律。
四、案例与常见误区复盘
某行情网关最初用 JSON 传输逐笔成交,字段为 {"sym":"600000","px":10.25,"qty":100}。随着标的数量增长,带宽和 GC 压力上升,团队迁移到 Protobuf。迁移中出现的典型问题值得复盘:
- 字段编号复用:删除旧字段
px后,新字段price复用了编号 2,导致旧消费方把价格解析成数量。正确做法是reserved 2;。 - 默认值语义混淆:Protobuf 3 中数值字段无法区分“未设置”和“0”,行情中价格为 0 与缺失含义完全不同,需用
optional或 wrapper 类型。 - 精度丢失:用
float表示价格,在 10.25 等十进制小数上出现舍入误差,应改用定点整数或decimal字符串。 - 缺少兼容策略:Schema Registry 未配置兼容级别,一次“加必填字段”的变更直接让下游反序列化失败。
综上,Schema 基础概念的核心在于:结构定义数据长什么样,元数据定义数据如何被治理,模式语言决定数据如何被序列化与演进。只有把三者统一为数据契约,行情系统才能在频繁变更中保持稳定与兼容。