本文是《Designing Data-Intensive Applications》(DDIA,中文译名《数据密集型应用系统设计》)第 12 章的导读。DDIA 是 Martin Kleppmann 所著的分布式系统经典,本系列逐章导读,把书的核心概念讲清楚。
一句话主旨
单个数据库不是孤立存在的——它总是某个更大数据流的一环。数据从源头经过若干转换落到多个目标系统,每个系统都是同一个底层数据的"派生视图"。DDIA 终章的核心是:如何让这些派生视图保持一致、可溯源、可重算?原书重心不在预测而在综合——以日志为中心把异构系统粘起来,并用端到端正确性保证跨系统一致。
核心概念拆解
1. 派生数据(Derived Data)——本章的核心概念
派生数据的关键属性:
- 可从源头重算:派生视图不是"源数据",丢了能从源头重建。只有源头是"事实之源"(source of truth)。
- 冗余但一致:多个视图存的是同一数据的不同侧面,冗余但应逻辑一致。
- 更新有延迟:源头变了,派生视图要时间同步(批有批的延迟,流有流的延迟)。
当一个系统存的数据能从另一个源头重新生成,它就是派生视图而非事实之源。识别哪些是派生数据、哪个是源头,是数据集成设计的起点。
为什么不能直接双写?很多人想"源头变了,同时写多个目标系统"来保持一致——这叫双写,是派生数据纪律的核心病因。跨系统无法原子提交,部分失败导致副本永久分叉:写系统 A 成功、写系统 B 失败,A 有了新值 B 还是旧值,之后没有机制自动修复,分叉就此固化。正确做法是单写源头 + 变更传播:只写源头,用 CDC 或批处理把变更传播到派生视图,传播可重放、可对账。
2. Lambda 架构 vs Kappa 架构——批流如何统一
Lambda 架构:
- 批层:定期全量重算(准确但慢)
- 流层:实时增量(快但可能不准/有错)
- 服务层:查询时合并批结果 + 流增量
- 问题:维护两套代码(批 + 流),逻辑要重复实现,易不一致
Kappa 架构:
- 只有流处理一层(但流可重放且保留足够历史,所以能"回到过去"重算全量)
- 用可重放的消息日志(Kafka)当源头,重算 = 回退 offset 重放
- 优势:一套代码,只有流逻辑
- 条件:消息可重放(Kafka)+ 流处理支持状态重算,且保留足够历史
Lambda 的痛点是两套代码要保持逻辑一致——批层改了流层也要改,容易漂移。Kappa 用一套流代码替代,但要求消息可重放和流处理能重算状态。
3. 数据库的 unbundling——"数据库"是多个组件的打包
DDIA 一个深刻观点:传统数据库内部做的很多事(索引、物化视图、缓存、复制),在分布式数据栈里被"解包"成独立组件。
Unbundling 的含义:
- 传统数据库把存储/索引/物化视图/缓存/复制打包在一个系统里,用事务保证一致
- 分布式栈把这些拆成独立系统,每个可独立选型、独立扩展、独立演进
- 代价:跨系统没有事务,一致性要应用自己保证
- 好处:每个组件可独立选型和扩展
趋势本质:数据库内部的机制(索引、物化视图、CDC、复制)正在变成独立工具。未来不是"选一个数据库",而是"选一组工具 + 自己设计集成"。这降低了组合门槛(任何组件可替换),也提高了要求(要懂数据库内部原理才能组合好)。其中日志(如 Kafka)是核心黏合剂——数据库内核早就在用日志(WAL/redo log)做复制和恢复,unbundling 只是把这根日志从数据库内部拉出来,变成跨系统的统一变更流。
Unbundling 后跨系统没有事务,端到端正确性要应用自己保证。集成纪律是用管线+调度+契约部分替代失去的事务。
4. 端到端正确性(End-to-End Correctness)
派生数据最容易出的问题:源头变了但派生视图没跟上(或反过来)。DDIA 强调端到端正确性——从源头到派生视图,数据应"不多不少"地一致。DDIA 借用 Saltzer 的端到端论据:跨系统的"恰好一次"只能靠端到端保证,不能指望中间任何单跳。
端到端正确性的保障手段:
- 唯一标识贯穿:源头和派生视图用同一 id,能对账
- 可重算:派生视图能从源头重建,出错了能重算修复
- 对账机制:定期校验源头数 vs 派生数,发现不一致
- 幂等导入:重跑不重复
派生数据最容易"看起来对实际错"——中间结果表面正常,但和源头对不上。只有端到端校验能兜底。不信任派生结果、回到源头验证,是数据集成的核心纪律。
问题→方案:问题——同一数据要派生成多个视图(元数据表、全文索引、聚合仪表盘),跨系统没有事务保证一致。场景——分布式数据栈把传统数据库内部的索引/物化视图/缓存"解包"成独立组件,各组件独立选型但失去跨系统事务。方案——派生数据纪律:源头是唯一事实之源,派生视图可重算;用唯一标识贯穿实现对账,定期校验源头数 == 派生数;幂等导入保证重跑不重复。Lambda 用批+流两层(两套代码易漂移),Kappa 用一层可重放流(一套代码但要求消息可重放)。
5. 数据血缘(Data Lineage)——追踪数据从哪来
血缘的价值:
- 排障:派生视图出问题,顺着血缘回溯到源头定位
- 影响分析:源头某字段变了,知道影响哪些派生视图
- 合规:数据从哪来、经过什么处理
6. 批/流做集成——同步 vs 异步派生
| 批 | 流 | |
|---|---|---|
| 更新 | 定期全量/增量 | 实时持续 |
| 延迟 | 分钟~小时 | 毫秒~秒 |
| 复杂度 | 低 | 高(CDC/状态/水位线) |
| 一致性窗口 | 大(批之间不一致) | 小(近实时) |
一致性窗口多小才值得上流——如果场景不需要近实时,批同步完全够,上流是过度工程。流的复杂度成本只在"批给不了的延迟"时才值得付。越来越多系统从"批同步"转向"流式 CDC",把一致性窗口从小时缩到秒,但这按需推进,不盲目上 CDC。
尾声:原理长存,工具会换
DDIA 终章不教新技术,而是给前 11 章画一个方向坐标。最大的趋势是把数据库内部的技术(流、CDC、物化视图、端到端正确性)变成通用工具,让任何应用都能用上。端到端正确性将成为应用层的通用要求——unbundling 后跨系统没有事务兜底,催生工具和框架让应用层声明"数据应满足什么不变量"、系统自动校验。
导读补充观点:数据工具越来越易用,让非专家也能用,但底层原理的重要性不变——工具替你做了决策,你要懂原理才能判断工具的决策对不对。懂原理让你能快速评估新工具、判断它的取舍、预判它的坑。工具会换,原理长存——学 DDIA 的回报正是"工具换了一茬仍能快速评估取舍"。
DDIA 最后提到数据伦理与隐私:数据收集、使用、隐私、偏见。这不是纯技术,但作为数据基础设施工程师,要意识到数据的影响——数据从哪来、能否用、有什么影响。
DDIA 12 章导读至此全部完成。