数据密集型系统设计 — 知识框架¶
梳理自《Designing Data-Intensive Applications》(DDIA / 数据密集型应用系统设计) 定位:框架性地图,不是逐章翻译;各节点链到本专题已有文档,空白处标记待沉淀 用法:按三大篇自上而下建立全局观,再按需深潜单章
← 返回 数据工程专题索引
文档与学习约定¶
本专题文档结构¶
| 类型 | 文件 | 说明 |
|---|---|---|
| DDIA 总脑图 | ddia-master-mindmap | 顶层索引:7 层纵轴 + 场景/组件/排障/成本 + Canvas |
| DDIA 地图 | 本文 ddia-framework.md |
三大篇框架 + 6h 时间轴(不含 Ch.3 正文) |
| Ch.3 存储与检索 | ddia-ch03-storage-retrieval | 存储引擎全景、哈希/B+Tree/LSM、MVP + 6h 第 2 段 45min |
| Part 2 分布式 | ddia-part2-distributed-systems | Ch.5–9:复制/分区/事务/一致性 + 完整第二篇 150–180min |
| Ch.10 批处理 | ddia-ch10-batch-processing | 历史→挑战→解法 + 6h 第 3 段 60min |
| Ch.11 流处理 | ddia-ch11-stream-processing | 历史→挑战→解法 + 6h 第 4 段 90min |
| 算法侧 | overview、数组、双指针、7 天冲刺 | overview 已链入数组 / 双指针专题 |
| 组件路径 | dbt、Flink、ClickHouse 等 | 见 组件 × 框架对照表 |
笔记写法(默认约定)¶
- 讲解、答疑:一般 补进对应 md(不散落在对话里)
- 有疑问 / 纠错:记入本文 QA 疑问解答 — 固定格式 ❌ 我的理解 + ✅ 纠正(见该节模板)
- Ch.1 及同类章:原始笔记(
<details>可折叠)+ 结构化总结 双轨保留,不删原话
DDIA 学习节奏¶
| 事实 | 做法 |
|---|---|
| 全书 ~500 页 | 一天读完不现实 |
| 6h 目标 | 地图 + 核心章:Ch.1、Ch.3 跳读、Ch.10、Ch.11(Ch.5–6 只读小结可选) |
| 头晕 / 疲劳 | 用 低电量模式 |
| Ch.3 里 LSM | 15–20 分钟 概念够用;不必再深挖实现,MVP 代码留着以后看 |
当前进度(滚动更新)¶
| 阶段 | 状态 | 下一步 |
|---|---|---|
| Ch.1 可靠 / 扩展 / 维护 | ✅ | — |
| Ch.3 哈希索引 + LSM 概念 | ✅ | LSM 跳读 15–20min 即可,实现细节改天 |
| Part 2 分布式(Ch.5–9) | 🔜 | 复习专题 |
| Ch.10 批处理 | 🔜 | 复习专题 + Airflow / dbt |
| Ch.11 流处理 | 🔜 | 复习专题 + Flink |
问题抽象:四维分类(清醒日用)¶
头晕时 不要硬啃 分类表;清醒日用来拆题。完整表见 数据结构与算法 § 问题的抽象分类。
| 维度 | 在问什么 | 数据工程例子 |
|---|---|---|
| 目标类型 | 判定 / 检索 / 聚合 / 优化… | 对账 → 匹配;报表 → 聚合 |
| 数据关系 | 线性 / 键值 / 层次 / 网络… | 明细分区 → 线性+区间;维度 → 层次 |
| 核心操作 | Access / Search / Insert / Scan… | OLAP 重 Scan;OLTP 重 Search + 行更新 |
| 求解策略 | 双指针 / 滑动窗口 / 二分 / 分治… | 批 → 分而治之;流 → 窗口 + 状态 |
Ch.3 存储引擎 — 主题速查(锚点在专题文)¶
全文 → ddia-ch03-storage-retrieval.md
| 主题 | 要点 | 锚点 |
|---|---|---|
| 全景串讲 | 历史 → 挑战 → 解法 | § 串讲 |
| 哈希 / Redis | 点查、过期风暴、Bitcask 对比 | § 哈希 |
| B+Tree / 页 | 叶子、分裂、O(log n) | § 图解 · § B+Tree |
| LSM / SSTable | 三个放大、compaction | § LSM |
| MVP | Java 三档对照 | § MVP |
| 6h 第 2 段 | 45min 清单 | § 清单 |
Ch.10 / Ch.11 派生数据 — 主题速查¶
| 主题 | 要点 | 专题文档 |
|---|---|---|
| 派生数据 | 从源数据 算出第二份(批/流都是) | 两篇全景 §0 |
| 批处理 | 有界、幂等、可重跑、不可变分区 | Ch.10 专题 |
| 批处理架构 / MVP | 应用图 + 系统内部图;SimpleBatchJob / SimpleBatchDag |
Ch.10 架构 · MVP |
| 流处理 | 无界、事件时间、Watermark、状态 | Ch.11 专题 |
| 流处理架构 / MVP | 应用图 + 系统内部图;SimpleStreamPipeline |
Ch.11 架构 · MVP |
| 6h 第 3 段 | 60min 清单 + 批处理 4 步 | § 第 3 段清单 |
| 6h 第 4 段 | 90min 清单 + 流 vs 批 3 行 | § 第 4 段清单 |
| 流批对照 | 延迟 / 输入 / 容错 | Ch.11 完整表 |
Part 2 分布式 — 主题速查¶
全文 → ddia-part2-distributed-systems.md
| 主题 | 要点 | 锚点 |
|---|---|---|
| 全景串讲 | 复制 → 分区 → 事务 → 故障 → 一致性 | § 串讲 |
| 复制 | Leader/Follower、异步延迟、读己之写 | § Ch.5 |
| 分区 | 哈希/范围、热点、与 Kafka/Flink 对齐 | § Ch.6 |
| 事务 / 对账 | 少用 2PC;幂等 + 日批收敛 | § Ch.7 |
| 一致性 / EOS | 谱系、Quorum、Flink Checkpoint | § Ch.9 · 三角表 |
| 完整第二篇 | 150–180min 清单 | § 清单 |
| 6h 第 5–6 段 | Ch.5–6 + Ch.7/9 选读 | § 6h 捷径(6h 内可只走折叠捷径) |
备忘:dbt = SQL 数仓工程化工具,不是数据库 → dbt-learning-path;双指针 Java 两版命名(low/high vs left/right)算法相同;++i / i++ 单独一行无差别。
框架总览¶
数据密集型系统设计
├── 第一篇 数据系统基础 ← 单机视角:可靠、模型、存储、编码
├── 第二篇 分布式数据系统 ← 多机视角:复制、分区、事务、一致性
└── 第三篇 派生数据 ← 批 / 流:从原始数据算出第二份数据
├── 批处理
├── 流处理 → [Flink](./flink-streaming.md)
└── 未来趋势
一句话:现代数据工程 = 在 不可靠的分布式硬件 上,用 合适的数据模型与存储,通过 批或流 把原始数据 派生 成业务可用的表、指标与服务。
总脑图(由点及面) → ddia-master-mindmap.md(§0 排障罗盘 · §1 需求九宫格 · §4 下钻至内存/磁盘);交互版 Canvas:ddia-master-mindmap.canvas.tsx
第一篇:数据系统基础¶
还没上分布式之前,先弄清:应用要可靠、数据要有模型、磁盘要怎么查、格式要怎么演进。
目前大部分系统都是数据密集型应用,而不是计算密集。通常包含以下模块 - 1. 数据库:存储数据,方便下次查询访问 - 2. 高速缓存:缓存复杂、操作代价高的结果,以加快一下次的访问 - 3. 索引:按照关键字搜索数据并支持各种过滤 - 4. 流式处理: 持续发消息到另一个进程,而处理采用异步的方式 - 5. 批处理:定期处理大量的累积数据
共性的目标:可靠、可扩展、可维护的数据系统
如何达成这个目标,如何在拆解分析业务需求、架构需要,选择合适的组件呢?慢慢展开
典型数据系统架构示例¶
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 应用代码 │────▶│ 主数据库 │ │ 外部系统 │
└─────────────┘ └─────────────┘ └─────────────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ 高速缓存 │◀────│ 从数据库 │
└─────────────┘ └─────────────┘
│
▼
┌─────────────┐ ┌─────────────┐
│ 全文索引 │────▶│ 消息队列 │
└─────────────┘ └─────────────┘
| 模块 | DDIA对应 | 本系统组件 |
|---|---|---|
| 应用代码 | 业务逻辑层 | API / 服务 |
| 主数据库 | 存储引擎 | PostgreSQL / MySQL |
| 高速缓存 | 缓存层 | Redis / Memcached |
| 从数据库 | 复制/读扩展 | ClickHouse Replica |
| 全文索引 | 搜索 | Elasticsearch / OpenSearch |
| 消息队列 | 异步/流处理 | Kafka / RabbitMQ |
| 外部系统 | 第三方集成 | 支付/短信等 |
1.1 可靠、可扩展、可维护的应用系统¶
你的原始笔记(保留)¶
点击展开你的原始笔记(完整保留)
问题: 1. 局部故障失效 2. 系统降级 3. 负载增加 4. 友好的api设计 | 维度 | 核心问题 | 工程里对应什么 | |------|----------|----------------| | **可靠** | 故障时仍正确、不丢数 | 重试、幂等、对账、Dead Letter | | **可扩展** | 负载变大怎么扛 | 垂直扩容、水平分片、读写分离 | | **可维护** | 新人能否看懂、能否改 | 分层数仓、dbt 文档、口径单一事实源 | | **性能** | 延迟 vs 吞吐 | OLTP 毫秒 vs OLAP 扫描 GB/s | | **数据系统负载** | 读多还是写多、查询模式 | 点查 / 范围扫 / 聚合 / 搜索 | 可靠性 期望: 1. 实现了基本的功能需求 2. 容忍用户出现错误、不正确的软件使用 3. 性能能够应对典型场景、合理的负载压力和数据量 4. 系统可以防止任何未经授权的访问和滥用 不太可能规避所有故障,但是要保证系统不能失效 现实: 1. 硬件故障 2. 软件故障: a. 软件bug, 程序设计有遗漏 b. 共享资源不足,程序跑飞 c. 依赖的外部服务变慢、故障等,因此带来更多的级联故障 3. 人为失误,我们需要假定人是不可靠的,因此需要最小出错的方式来设计系统;分离出最容易出错的部分,容易引发故障的接口,严格隔离生产、测试环境;需要充分的测试后再上线;有快速回复的应急机制、迅速预案,先止损在修复;设置详细清晰的监控子系统,持续监控告警、检查假设不成立的case,违反约束条件的case; 4. 推行管理流程并加以培训 可扩展性 可扩展性是描述系统在应对负载增加时表现如何的术语, 当系统以某种方式增长,我们的应对措施有哪些? 我们应该如何评估负载,如果计算需要因此增加多少资源来承载 描述负载 有多少用户同时使用平台功能,有多少读、写同时执行,重点关注背后带来级联读写 描述性能 1. 负载增加,资源不变,系统性能变化是? 2. 负载增加,性能不变,需要增加多少资源 批处理更关注吞吐量,在线、streaming 在意延迟 平均数、中位数、p95、p99、p999 SLA、SLO 可维护性 过时的系统、中间暴露的bug等 1. 监控 2. 故障排查 3. 适配新平台、单配新场景 4. 技术缺陷完善增加新功能 可运维性:方便快速恢复、定位问题 简单性:简化复杂度 可演化性:易于改变结构化总结(在你笔记基础上的补充)¶
你这章可以先记成 4 个工程问题:
- 局部故障时,系统会不会整体失效?
- 故障发生时,如何降级而不是雪崩?
- 负载增长时,系统如何扩展?
- API 和系统边界是否易用、稳定、可演化?
| 维度 | 核心问题 | 工程里对应什么 |
|---|---|---|
| 可靠 | 故障时仍正确、不丢数 | 重试、幂等、对账、Dead Letter |
| 可扩展 | 负载变大怎么扛 | 垂直扩容、水平分片、读写分离 |
| 可维护 | 新人能否看懂、能否改 | 分层数仓、dbt 文档、口径单一事实源 |
| 性能 | 延迟 vs 吞吐 | OLTP 毫秒 vs OLAP 扫描 GB/s |
| 数据系统负载 | 读多还是写多、查询模式 | 点查 / 范围扫 / 聚合 / 搜索 |
可靠性¶
期望(你总结得很对):
- 功能正确
- 用户误操作可容错
- 典型负载下性能稳定
- 能防止未授权访问和滥用
现实里无法消灭所有故障,但要做到:故障可控、可发现、可恢复。
常见故障源:
- 硬件故障(机器、磁盘、网络)
- 软件故障
- bug、边界条件遗漏
- 共享资源耗尽(线程池/连接池/内存)
- 外部依赖变慢导致级联故障
- 人为失误(错误发布、错误配置、误删数据)
- 组织流程不足(无演练、无预案、无培训)
应对策略(可当实践清单):
- 隔离最容易出错的模块与接口,防止故障扩散
- 严格区分生产/测试环境,发布前做充分验证
- 建立应急预案:先止损、再修复、再复盘
- 构建可观测性:监控、告警、日志、追踪
- 持续检查「假设不成立」和「违反约束」的 case
可扩展性¶
可扩展性是描述系统在应对负载增加时表现如何的术语, 当系统以某种方式增长,我们的应对措施有哪些? 我们应该如何评估负载,如果计算需要因此增加多少资源来承载
描述负载¶
有多少用户同时使用平台功能,有多少读、写同时执行,重点关注背后带来级联读写
描述性能¶
- 负载增加,资源不变,系统性能变化是?
- 负载增加,性能不变,需要增加多少资源
批处理更关注吞吐量,在线、streaming 在意延迟
平均数、中位数、p95、p99、p999 SLA、SLO
可维护性¶
过时的系统、中间暴露的bug等 1. 监控 2. 故障排查 3. 适配新平台、单配新场景 4. 技术缺陷完善增加新功能
可运维性:方便快速恢复、定位问题¶
简单性:简化复杂度¶
可演化性:易于改变¶
可维护性可再记成三件事:
- 可运维性(Operability):出问题能快速定位与恢复
- 简单性(Simplicity):控制复杂度,降低心智负担
- 可演化性(Evolvability):需求变更时能低风险迭代
本专题关联
- 计费对账、日批报表 → 可靠 + 可维护 优先于极致延迟
- dbt 数据建模 → 可维护性(分层、测试、文档)
待沉淀
- 可靠性清单:幂等键、At-least-once vs Exactly-once 在数仓里的含义
- 扩展性案例:ClickHouse 分片 vs 应用层分库
1.2 数据模型与查询语言¶
| 模型 | 典型系统 | 擅长 | 弱项 |
|---|---|---|---|
| 关系型 | PostgreSQL、ClickHouse(SQL) | 关联、聚合、声明式 | 深层图遍历、灵活 schema |
| 文档 | MongoDB | 嵌套 JSON、快速迭代 schema | 跨文档 JOIN |
| 图 | Neo4j | 关系网络、路径 | 大规模聚合报表 |
| 键值 / 宽列 | Redis、Cassandra | 高吞吐点读写 | 复杂查询 |
核心对比
- 声明式(SQL):说「要什么」,优化器决定怎么扫
- 命令式(代码):说「怎么做」,灵活但优化靠人
本专题关联
- 数仓日常 → 关系型 + SQL 为主
- ClickHouse 深度解析 → SQL + 列存物理模型
待沉淀
- 计费场景:星型模型 vs 宽表 vs 明细+汇总两层
- 语义层(Metric Layer)与 SQL 的关系
1.3 存储与检索¶
全文已拆出 → ddia-ch03-storage-retrieval.md(~1700 行:全景串讲、哈希、B+Tree 图解、LSM、Java MVP、Redis 生产)。
本页只保留 地图级摘要;6h 第 2 段清单在专题文首。
| 场景 | 存储思路 | 深潜 |
|---|---|---|
| OLAP 分析 | 列存、MergeTree | ClickHouse · Ch.3 §列存 |
| OLTP / 索引 | B+Tree 原地改页 | Ch.3 §B+Tree |
| 高写入 / 状态 | LSM 只追加 | Ch.3 §LSM · Flink |
| 缓存点查 | 哈希 | Ch.3 §哈希 |
两条族谱(背一句)
追加日志 + 哈希 → 点查 O(1),无范围
B+Tree / LSM → 有序;B+ 原地改页,LSM 追加后 compaction
Ch.3 主题速查 → 见 专题文目录 与文首 6h 第 2 段清单
1.4 编码与演化¶
| 主题 | 问题 | 实践 |
|---|---|---|
| 编码格式 | JSON / Avro / Protobuf 如何选型 | 流式 schema、字段兼容 |
| 向后兼容 | 新字段老消费者能否读 | optional、默认值、版本号 |
| 数据迁移 | 改 schema 如何不停机 | 双写、回填、dbt incremental |
| 批 vs 流 | 同一事件两种形态 | Kafka 消息 + 数仓表 |
本专题关联
- dbt incremental 模型 → schema 演进与回填
- connector / ingest 链路 → 事件编码与 schema registry(规划)
待沉淀
- 计费事件 JSON → 数仓宽表的字段演化案例
第二篇:分布式数据系统¶
全文已拆出 → ddia-part2-distributed-systems.md(Ch.5–9:历史串讲、双视角架构图、Java MVP、完整第二篇 150–180min 清单、批流分布式三角)。
本页只保留 地图级摘要;6h 第 5–6 段链到专题文。机器一定会挂、网络一定会抖。第二篇回答:数据拆到多台之后,怎么 复制、怎么 分区、怎么在 少用 2PC 的前提下用 幂等 + 对账 收敛。
| 小节 | 一句话 | 深潜 |
|---|---|---|
| 2.1 复制 | 高可用 + 读扩展;异步有延迟 | § Ch.5 |
| 2.2 分区 | 写扩展 + 裁剪;防热点 | § Ch.6 |
| 2.3 事务 | 管道少用 2PC;幂等 + 日批 | § Ch.7 |
| 2.4 分布式麻烦 | 网络/时钟/部分失败 | § Ch.8 |
| 2.5 一致性 | 谱系 + Quorum;共识给元数据 | § Ch.9 |
Part 2 主题速查 → 见文首 Part 2 分布式 — 主题速查 与专题 完整清单
待沉淀 → 见 Part 2 专题 · 待沉淀
第三篇:派生数据¶
派生数据 = 从一份数据(日志、业务库快照)计算得到 的另一份数据(汇总表、宽表、指标 API)。批处理和流处理是两种 计算引擎,不是两种数据。
专题文档(推荐先读全景串讲):
- Ch.10 批处理 — 复习专题(历史 → MR → Spark → dbt/Airflow)
- Ch.11 流处理 — 复习专题(历史 → Kafka → Flink / Watermark / Checkpoint)
原始数据(Source of Truth)
│
├─ 批处理(高吞吐、延迟小时~天) → [Ch.10 专题](./ddia-ch10-batch-processing.md) · [Airflow + dbt](./airflow-data-pipeline.md)
│
└─ 流处理(低延迟、持续计算) → [Ch.11 专题](./ddia-ch11-stream-processing.md) · [Flink](./flink-streaming.md)
│
↓
派生表 / 指标 / 服务只读副本
3.1 批处理系统¶
| 特征 | 说明 |
|---|---|
| 输入 | 有界数据集(某日分区、全量快照) |
| 容错 | 重跑分区、幂等写入 |
| 代表 | MapReduce、Spark、dbt + SQL、Airflow DAG |
详细内容 → ddia-ch10-batch-processing.md(含 6h 第 3 段 60min 清单)
3.2 流处理系统¶
| 特征 | 说明 |
|---|---|
| 输入 | 无界事件流 |
| 核心抽象 | 事件时间、窗口、状态、Watermark |
| 代表 | Flink、Kafka Streams、Spark Structured Streaming |
详细内容 → ddia-ch11-stream-processing.md(含 6h 第 4 段 90min 清单)
组件深潜 → Flink 实时计算
3.3 数据系统的未来(提纲)¶
DDIA 末章讨论:多模型组合、Unbundled 数据库、正确性 vs 性能、主权与隐私。作方向感即可,不必一次学完。
| 趋势 | 和数据工程的关系 |
|---|---|
| 组合式架构 | 不用一个库打天下:Kafka + Flink + ClickHouse + dbt |
| 流批一体 | 同一逻辑 SQL/JAR,两套 runtime |
| Lakehouse | 对象存储 + 表格式(Iceberg/Hudi)+ 引擎 |
| 数据契约 / 质量 | schema 治理、Great Expectations、dbt test |
| 实时数仓 | 明细流式入湖 + 分钟级汇总 |
本专题关联
- 当前栈已是 Unbundled:调度 Airflow、建模 dbt、OLAP ClickHouse、流 Flink
- Docker 部署 → 组件编排与本地联调
待沉淀
- 从 V1.0 批到 V1.2 流的演进路线图(与 flink-streaming 对齐)
组件 × 框架对照表¶
| DDIA 篇章 | 本专题已有文档 | 角色 |
|---|---|---|
| 存储与检索 | ClickHouse | OLAP、明细与汇总 |
| 数据模型 | dbt | 分层、指标、测试 |
| 批处理 | Airflow | 调度、依赖、回填 |
| 流处理 | Flink | 实时派生、CAPI |
| 部署运维 | Docker | 本地 / 测试环境 |
| 复制 / 分区 | Part 2 专题 · ClickHouse | 副本、分区键 |
| 事务 / 一致性 | Part 2 专题 · Flink | EOS、对账 |
一日 6 小时冲刺(今天按表执行)¶
目标:不是读完 500 页,而是 6 小时后能画出三大篇 + 说出和你栈的对应关系。
版本:按 Kleppmann《数据密集型应用系统设计》第 2 版 章号;中译本页码因排版不同,以「章 + 小节标题」为准。
节奏:每 50 分钟学习 + 10 分钟休息(喝水、走动、不刷手机)。
时间轴总览¶
| 时段 | 时长 | 做什么 | 产出(必须写下来) |
|---|---|---|---|
| 0 | 0:30 | 本文「框架总览 + 组件对照表」 | 手绘三大篇箭头图 1 张 |
| 1 | 1:00 | Ch.1 可靠、可扩展、可维护 | 3 个词:可靠 / 扩展 / 维护 各举 1 个你项目里的例子 |
| 2 | 0:45 | Ch.3 存储与检索(跳读) | 一句话:ClickHouse 更像列存还是 LSM |
| 3 | 1:00 | Ch.10 批处理 | 专题 60min:批处理 4 步 + DAG/表链路 |
| 4 | 1:30 | Ch.11 流处理 | 专题 90min:流 vs 批 3 行 + Flink 目录 |
| 5 | 0:45 | Ch.5–6 复制 + 分区 | Part 2 专题 前 55min 或 6h 捷径:Ch.5–6 填空 |
| 6 | 0:30 | Ch.7 或 Ch.9 二选一 + 收尾 | § Ch.7 / Ch.9;计费链路一致性三行 |
今天刻意不读(改天或低电量再碰):Ch.2 全文、Ch.4 全文、Ch.8 全文、Ch.12 全文。
若某章超时:优先保 Ch.1、Ch.10、Ch.11,Ch.5–9 只读章末 Summary。
第 0 段(0:00–0:30)地图¶
读哪里? 就在 本页 ddia-framework.md 里读两段,不用开 DDIA 书:
| 读什么 | 在本文件的哪里 | 大约行号 |
|---|---|---|
| 5 分钟排障罗盘(可选) | 总脑图 §0 | 遇线上问题先用 |
| 文档与学习约定 | 本页 ## 文档与学习约定(节奏、进度、Ch.3 速查) |
文首 |
| 框架总览 | ## 框架总览 下面三大篇树 + 一句话 |
见上节链接 |
| 组件对照表 | ## 组件 × 框架对照表 |
跳转 |
[框架总览](#框架总览) 这种写法 = 本页内跳转链接(点一下会滚到对应标题),不是另一个文件。
具体怎么做(30 分钟)
- 从文件 最上面 往下读,看到
## 框架总览,读完那棵 ASCII 树和「一句话」 - 先不要 读后面的「第一篇 / 第二篇 / 第三篇」长文(那是以后慢慢补的)
- 往下拉 到
## 组件 × 框架对照表,读完那张 7 行表(DDIA 概念 ↔ ClickHouse/dbt/Airflow/Flink) -
拿一张纸,画三个框:
基础→分布式→派生(批/流),旁边写上你熟悉的组件名 -
读完「框架总览」
- 读完「组件对照表」
- 画完三大篇草图
- 停:30 分钟到点,去翻 DDIA 纸质书 Ch.1
第 1 段(0:30–1:30)Ch.1 可靠、可扩展、可维护的应用系统¶
读法:正文通读,术语表、引用可跳。
必须搞懂的点
| 小节方向 | 问自己 |
|---|---|
| 可靠性 | 硬件/软件/人为错误;你司对账算不算一种「容错」 |
| 可扩展性 | 负载参数是什么(QPS?日分区行数?) |
| 可维护性 | dbt 分层是在解决可维护性吗 |
| 数据负载类型 | 你的计费表偏 读多写少 还是 写多读少 |
写下来(1 分钟)
可靠:例如 ___________
扩展:例如 ___________
维护:例如 ___________
- 完成 Ch.1
第 2 段(1:30–2:15)Ch.3 存储与检索(跳读)¶
目标(45min):能画出 哈希 → B+Tree → LSM 三条演化线,说出 ClickHouse / InnoDB / RocksDB 各站哪一格。
全文在 → ddia-ch03-storage-retrieval.md
从这里开始 → § 6h 第 2 段复习清单(45min)
- 完成 Ch.3 跳读
第 3 段(2:15–3:15)Ch.10 批处理¶
目标(60min):能画出 批处理历史链(单机 → MR → Spark → dbt/Airflow),写出 批处理 4 步 和一条真实 DAG/表链路。
全文在 → ddia-ch10-batch-processing.md
从这里开始 → § 6h 第 3 段复习清单(60min)
- 完成 Ch.10(勾选清单内各项)
第 4 段(3:15–4:45)Ch.11 流处理¶
目标(90min):全书 6h 最重一章;能填 流 vs 批 3 行,说出 事件时间 / Watermark / Checkpoint 各解决什么历史问题。
全文在 → ddia-ch11-stream-processing.md
从这里开始 → § 6h 第 4 段复习清单(90min)
- 完成 Ch.11(勾选清单内各项)
第 5 段(4:45–5:30)Ch.5 复制 + Ch.6 分区¶
全文在 → ddia-part2-distributed-systems.md
从这里开始 → § 完整第二篇清单(6h 内完成清单 0–55min 段,或 已有批流经验 · 捷径 中 Ch.5–6 部分)
读法:每章 先读开头 + 章末 Summary + 看图;正文挑 Leader/Follower、分区键 两节即可。
| 章 | 带走一句话 |
|---|---|
| Ch.5 | 复制为了 高可用 和 读扩展;异步复制可能 丢/延迟 |
| Ch.6 | 分区为了 水平扩展 和 裁剪扫描;警惕 热点 |
和计费的关系:对账差异有时来自 从库延迟、分区扫错范围。
- Ch.5 Summary
- Ch.6 Summary
第 6 段(5:30–6:00)Ch.7 或 Ch.9 + 收尾¶
全文在 → ddia-part2-distributed-systems.md
从这里开始 → § Ch.7 事务 或 § Ch.9 一致性与共识
二选一(时间只够一个):
| 选项 | 适合你如果… |
|---|---|
| Ch.7 事务 | 想搞清 ACID、隔离级别、分布式事务为什么少用 |
| Ch.9 一致性与共识 | 更关心对账、最终一致、Kafka/Flink 语义 |
读法:只读 前半 + Summary,不抠 Raft 证明。
收尾 10 分钟(必做)
6 小时后的 realistic 预期¶
| 能做到 | 做不到 |
|---|---|
| 全书 结构 和 ~40% 章节 有印象 | 500 页 逐字读完 |
| 批 / 流 / 存储 / 复制 / 分区能 串到你栈 | 每章习题级细节 全记住 |
| 知道 下一步 读 ClickHouse / Flink 哪篇 | 代替 动手 做 DAG 或 Flink 作业 |
明天建议(可选 1h):Ch.2 数据模型 或 Ch.4 编码 — 和你 schema 演化更相关,不要和今天抢在同一个晚上。
建议学习顺序¶
阶段 0 地图(本文) ← 1 小时,知道三大篇各解决什么
阶段 1 第一篇 1.1 + 1.3 ← 可靠 + 存储,结合 ClickHouse
阶段 2 第三篇 3.1 批处理 ← Airflow + dbt,贴近日常工作
阶段 3 第二篇 2.1–2.2 复制与分区 ← [Part 2 §Ch.5–6](./ddia-part2-distributed-systems.md#ch5-复制)
阶段 4 第三篇 3.2 流处理 ← Flink 深潜
阶段 5 第二篇 2.3–2.5 事务与一致性 ← [Part 2 §Ch.7–9](./ddia-part2-distributed-systems.md#ch7-事务) · 与对账、EOS 串联
阶段 6 1.2 / 1.4 + 3.3 ← 模型选型、编码演化、趋势
低电量模式:只读「框架总览 + 组件对照表 + 某一篇提纲」,不新开组件。
待完善索引(按优先级)¶
| 优先级 | 主题 | 建议产出 |
|---|---|---|
| P0 | 日批计费 DAG 全链路 | 独立文档 + 链到 Airflow / dbt |
| P0 | 对账与一致性语义 | 链到 双指针 §二 |
| P1 | ClickHouse 分区 / 副本 vs DDIA | 补充进 clickhouse-deep-dive |
| P1 | 流批指标对账 | 新文档或 flink-streaming 章节 |
| P2 | Schema 演化案例 | 补充进 dbt-learning-path |
| P2 | Lakehouse / Iceberg 是否引入 | 3.3 趋势决策记录 |
QA 疑问解答¶
对话里的疑问与纠错 都落在这里,便于复习时对照「错在哪」。
正文仍写结构化知识;QA 只保留 你的原话式误解 + 纠正,不重复长文。
条目模板(以后照抄):
### Qn:一句话标题(日期 可选)
**关联**:[正文锚点](#...) 或 章节名
❌ **我的理解(错 / 不完整)**
- …
✅ **纠正**
- …
Q1:为啥先有 B+Tree?数据不都该顺序读写吗?(2026-05-29)¶
关联:§ 为啥会有 SSTable / LSM-Tree?(历史动机)
❌ 我的理解(错 / 不完整)
- 天然数据存取都是 顺序 的,所以 LSM 更合理,B+Tree 像历史选错了。
- 「机械盘随机写极慢;SSD 仍比顺序写贵」—— 只记住结论,不懂为啥。
✅ 纠正
- 顺序扫描文件 ≠ 按 key 更新一行。
UPDATE … WHERE id=12345在 B+Tree 里是 定位到某一磁盘页再改,对磁盘是 分散的页级 I/O,不是从头到尾顺序写。 - 先有 B+Tree(~1972):当时主流是 OLTP——点查、范围扫、行级原地更新、事务;B+Tree 为 读 + 页对齐 + 范围扫 设计,不是为「每秒百万条只追加」设计。
- 后有 LSM(~1996):日志/埋点/KV 写多读少、只追加 成为主矛盾,才用 只追加 + 后台 compaction 换写吞吐。
- 随机写慢:HDD = 磁头 seek;SSD = 无寻道,但 碎小原地改 仍有擦写/写放大 → 顺序写仍更划算,差距缩小了,没消失。
- 不是谁更「符合物理规律」,是 负载分工(余额高频改 → B+Tree;埋点灌库 → LSM)。
Q2:Log-structured 是不是「只管写 + 后台合并」,读用 Hashtable O(1)?(2026-05-29)¶
关联:§ 30 秒版(LSM) · § 哈希索引 · 三档对照
❌ 我的理解(错 / 不完整)
- Log-structured 把 B+Tree 一个写动作 拆成:① 只管写;② 后台公共模块做合并/压缩。
- 查询靠 内存 Hashtable,所以 O(1) 读。
✅ 纠正
| 说法 | 对错 |
|---|---|
| 写路径:前台只追加 + 后台 compaction 整理(和 B+Tree 原地改页 解耦) | ✅ 核心对 |
| 读路径:Hashtable O(1) | ❌ 这是 哈希索引(Redis / Bitcask),不是典型 LSM |
- LSM 内存:memtable 是 有序结构(跳表/红黑树等),不是 HashMap;点查约 O(log n),且可能要查 memtable + 多层 SSTable,从新到旧合并 最新版本 → 读放大,不是 O(1)。
- Hashtable O(1):
HashMap<key, offset>+ 追加 log(Tiny Redis / Bitcask)—— 另一条路线,和 LSM 正交,别混。 - 一句话:Log-structured 主要换的是 写模型;LSM 的代价在 读要多层查 + 合并,不是 Hashtable 那点查。
Q3:「有序」是事件时间还是插入时间?排序了还要索引吗?(2026-05-29)¶
❌ 我的理解(错 / 不完整)
- memtable / SSTable 的「有序」= 事件时间 或 插入时间 顺序。
- 既然已经 排好序 了,范围查询 直接扫就行,还要索引干什么?
✅ 纠正
- 有序 = 按排序键(sort key / primary key)的字节序,由建表/引擎配置决定;不是默认 event time / insert time。
- 要把事件时间当顺序,必须把
event_time放进 key(如(user_id, event_time));Flink event time ≠ RocksDB 物理顺序。 - 排序的目的:① compaction 多路归并;② 范围扫;③ 配合 稀疏索引 / Bloom 缩小区间、少读盘。
- 「还要索引吗」:B+Tree 整棵树就是索引;SSTable = 有序文件 + 文件内稀疏 key→offset;哈希才是 无顺序、只点查。排序与索引是 一体设计,不是二选一。
Q4:B+Tree 里的「页」怎么理解?(2026-05-29)¶
关联:§ 页(page)怎么理解? · SimpleBPlusTree MVP
❌ 我的理解(错 / 不完整)
- 「页」= 书的一页 / 网页?
- MVP 里的
Page类 就是 磁盘上的东西,和内存无关?
✅ 纠正
- 页 = 存储引擎与磁盘之间的 固定大小数据块(如 16KB);一次 I/O 常读/写一整页。
- B+Tree 节点 在实现里 通常 1 节点 = 1 页(根页、叶子页说的都是这个)。
- 内存里有一份拷贝在 buffer pool(页缓存);改数据是 先改内存里的页,再 刷脏页 回磁盘。
- MVP 的
Page= 只保留逻辑结构(keys/children),没画磁盘偏移和缓存,但对应生产里的「一页节点」。 - 不是 LSM 那种「只追加新文件」;B+Tree 更新常是 改已有页 → 随机写页。
Q5:B+Tree 读写都是 O(log n) 吗?为啥?(2026-05-29)¶
❌ 我的理解(错 / 不完整)
- 读、写 都是 O(log n),但 不知道为什么。
- log 的底是 2?和二分一样?
✅ 纠正
- n = key/行数;m = 每页扇出(一页大约 m 个分支);树高 h ≥ log_m(n) → 写成 O(log n)(m 当常数)。
- 点查 / 插入:从根到叶 最多 h 层,每层读/写 1 页 → 磁盘 I/O O(h) = O(log n)。
- 插入最坏:页满分裂 最多传到根,再 O(h),总和仍 O(log n) 页级别。
- 范围查询:O(log n + k),k 是结果条数,不是纯 log n。
- log 底:大 O 里通常 不写底;实现里底是 扇出 m(几百),所以 h 很小,不是 log₂(n) 那种「一层只分两半」的二叉树。
小结¶
| 篇 | 记住一句 |
|---|---|
| 基础 | 单机时代的数据模型、存储引擎、编码演化 |
| 分布式 | 复制、分区、事务、一致性——接受故障是常态 |
| 派生 | 批与流都是 从源数据算出第二份数据;Flink 是流这一路的主引擎 |
程序 = 数据结构 + 算法(算法面试)在数据工程侧的镜像:数据系统 = 存储模型 + 派生管道(批/流)。