题干与适用场景
把它当作数据工程设计题。假设峰值每秒 2,000 个订单事件、新鲜度目标 15 分钟、必填字段完整率目标 99.5%。契约需要覆盖结构、含义、质量、服务等级、责任人、隐私标签,以及如何修改承诺。
边界是上游生产者与下游消费者之间的数据产品。数据库表定义太窄,无法说明 total_amount 是否含税、谁负责数据流,或错过新鲜度目标后如何处理。
面试官考察点
面试官要听到一个可执行、有版本、有责任人的契约。只列出 Avro 或 JSON Schema 属于弱回答;强回答会区分兼容性与语义正确性,把校验放在生产边界,并给消费者明确的违约与迁移路径。
你需要把每个字段连到消费者决策:拒绝、隔离、转换、告警,或带着明确的降级状态继续。还要解释怎样避免契约变成无人维护的审批队列。
回答前需要澄清的问题
- 来源是只追加事件流、可变表,还是两者都有?可变快照还需要键、更新语义和删除规则。
- 哪些保证是发布闸门?付款事件的货币无效应拒绝,非必需营销标签则可隔离。
- 消费者能否落后一版?可以的话要提供兼容窗口和转换;不可以则需要协调切换。
- 事件能否重放,是否含个人资料?重放能力影响保留期,隐私标签影响脱敏、访问和删除。
30 秒回答框架
“我会先从消费者用例出发,写一份有版本的机器可读契约。它定义 Schema 与语义、必填字段和域校验、新鲜度与完整率 SLO、责任人、隐私标签和演进策略。生产者发布前校验,注册中心在 CI 检查兼容性;运行时把坏记录隔离并暴露指标。新版本默认采用增量演进,语义破坏时用双写或转换窗口。每次违约都有负责人、重放路径和消费者可见状态。”
分步骤深入解答
1. 定义契约对象
为数据集设置标识与版本。每个字段记录类型、可空性、单位、业务含义、允许值、敏感级别,以及是否允许未知字段。订单事件要说明 occurred_at 是事件时间,金额是否为最小货币单位,以及取消订单是否仍可见。
加入责任人、支持渠道、保留期、新鲜度、交付频率和可用性。服务等级必须可测量:新鲜度可以是最新合格记录的年龄,完整率可以是窗口内必填字段非空比例。
2. 按失败模式拆分校验
Schema 校验发现缺字段和不兼容类型;域校验发现负金额或不支持的货币;关系校验发现重复事件 ID 或跳过状态的订单;新鲜度和量级校验发现生产者停发或分区不完整。
将结果连同契约版本、生产者构建版本、分区、样本窗口和失败规则保存。这样消费者才能决定暂停、回填,或接受有边界的降级。
3. 在发布前后执行
CI 中把候选 Schema 与注册版本比较,并用样例数据运行契约测试。运行时在事件进入共享流前于生产者边界校验。坏记录进入隔离流,保留原始载荷、失败规则、契约版本和重放键。
消费者仍应检查关键不变量。生产端校验能预防很多事故,但消费者检查可以保护路由错误、旧生产者和转换缺陷。
4. 把演进写成规则
只有旧消费者能容忍未知字段时,新增可选字段才可能保持兼容。重命名会破坏契约,因为字段名或含义改变。优先采用新增并弃用:发布新字段,双写或转换,迁移消费者,统计旧字段读取量,再按约定窗口下线。
例如把 total_amount 从含税改成未税,使用新版本或新字段比复用原名安全。若消费者必须继续使用旧视图,就提供版本化转换,并标记转换值。
5. 定义违约处理
使用严重等级。付款事件格式错误就拒绝并隔离;新鲜度违约通知负责人并标记数据过期;非关键描述失败则可继续但必须记录指标。契约要写明谁能临时放行、放行多久、需要什么证据。
不要静默丢弃。按生产者和契约版本记录接收、拒绝、隔离、重放和重复数量。重放必须幂等,接收端用事件 ID 与契约版本避免产生第二次业务效果。
6. 验证运行模型
用固定样例做契约测试,每个新版本做兼容性测试,再在生产分区抽样做金丝雀校验。覆盖迟到事件、重复 ID、未知枚举、必填字段为空、时区错误和生产者停止发送。
仪表盘要同时显示违约率、新鲜度年龄、完整率、消费者延迟、隔离深度、重放成功率和负责人确认时间。Schema 检查通过但数据过期,仍然代表数据产品失败。
高质量示范回答
“我会把订单流当成有版本的数据产品。先列出消费者并定义事件语义:事件时间、金额单位、货币、身份和状态转移。契约随后包含 Schema、域规则、关系规则、新鲜度与完整率 SLO、责任人、保留期和隐私标签。
“注册中心在 CI 拒绝不兼容变更。生产者发布前校验,运行时把坏记录连同失败规则和契约版本送进隔离流。消费者仍保留少量关键检查,因为路由或转换仍可能出错。
“我会默认采用增量演进。重命名或语义变化时新增字段或版本,双写、迁移消费者、统计旧字段读取量,再在兼容窗口结束后下线旧版本。新鲜度和质量违约都要有严重等级、负责人、告警和重放流程。最后用固定样例、金丝雀、迟到与重复事件,以及新鲜度、完整率、隔离深度和重放正确性指标验证。”
常见错误
- 错误表现 → 失败原因 → 修正方法: 把 Schema 当成完整契约 → 语义和责任人仍是隐含约定 → 补充含义、SLO、责任人和变更策略。
- 错误表现 → 失败原因 → 修正方法: 同步拒绝所有坏记录 → 一个坏事件可能阻塞整分区 → 用背压受控的隔离流和重放键处理。
- 错误表现 → 失败原因 → 修正方法: 认为新增字段总是安全 → 严格消费者可能无法处理未知字段 → 先验证消费者兼容性。
- 错误表现 → 失败原因 → 修正方法: 只告警 Schema 不匹配 → 过期或不完整数据仍可能通过 → 监控新鲜度、量级、完整率和业务不变量。
- 错误表现 → 失败原因 → 修正方法: 改变含义后复用字段名 → 历史值和新值无法比较 → 新建版本或明确转换字段。
追问及应对
如果生产者不能同时升级怎么办?
保留旧契约,新增字段或版本,在可衡量的窗口内同时接受两者。兼容适配器可以转换旧输入,但必须暴露信息损失和下线日期,不能隐藏差异。
契约测试通过但指标仍然错误怎么办?
这属于语义漂移。增加业务不变量或对账校验,与独立来源比较,并把争议定义写回契约。结构兼容不能证明生产者应用了正确业务规则。
如何避免隔离区变成数据坟场?
为每条规则指定负责人和处理目标,保留原始载荷与契约版本,统计队列年龄和重放成功率。每日复盘将失败归类为生产者缺陷、契约缺陷或预期例外。
什么时候不值得使用完整数据契约?
只有一个负责人、短期存在且没有下游承诺的私有表,可以只使用轻量 Schema 和测试。当多团队共享、需要重放、含受监管字段或有新鲜度承诺时,再引入完整契约。