跳转至

数据密集型系统设计 — 知识框架

梳理自《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 已链入数组 / 双指针专题
组件路径 dbtFlinkClickHouse 组件 × 框架对照表

笔记写法(默认约定)

  • 讲解、答疑:一般 补进对应 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 个工程问题:

  1. 局部故障时,系统会不会整体失效?
  2. 故障发生时,如何降级而不是雪崩?
  3. 负载增长时,系统如何扩展?
  4. API 和系统边界是否易用、稳定、可演化?
维度 核心问题 工程里对应什么
可靠 故障时仍正确、不丢数 重试、幂等、对账、Dead Letter
可扩展 负载变大怎么扛 垂直扩容、水平分片、读写分离
可维护 新人能否看懂、能否改 分层数仓、dbt 文档、口径单一事实源
性能 延迟 vs 吞吐 OLTP 毫秒 vs OLAP 扫描 GB/s
数据系统负载 读多还是写多、查询模式 点查 / 范围扫 / 聚合 / 搜索

可靠性

期望(你总结得很对):

  1. 功能正确
  2. 用户误操作可容错
  3. 典型负载下性能稳定
  4. 能防止未授权访问和滥用

现实里无法消灭所有故障,但要做到:故障可控、可发现、可恢复

常见故障源:

  1. 硬件故障(机器、磁盘、网络)
  2. 软件故障
  3. bug、边界条件遗漏
  4. 共享资源耗尽(线程池/连接池/内存)
  5. 外部依赖变慢导致级联故障
  6. 人为失误(错误发布、错误配置、误删数据)
  7. 组织流程不足(无演练、无预案、无培训)

应对策略(可当实践清单):

  • 隔离最容易出错的模块与接口,防止故障扩散
  • 严格区分生产/测试环境,发布前做充分验证
  • 建立应急预案:先止损、再修复、再复盘
  • 构建可观测性:监控、告警、日志、追踪
  • 持续检查「假设不成立」和「违反约束」的 case

可扩展性

可扩展性是描述系统在应对负载增加时表现如何的术语, 当系统以某种方式增长,我们的应对措施有哪些? 我们应该如何评估负载,如果计算需要因此增加多少资源来承载

描述负载

有多少用户同时使用平台功能,有多少读、写同时执行,重点关注背后带来级联读写

描述性能
  1. 负载增加,资源不变,系统性能变化是?
  2. 负载增加,性能不变,需要增加多少资源

批处理更关注吞吐量,在线、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):说「要什么」,优化器决定怎么扫
  • 命令式(代码):说「怎么做」,灵活但优化靠人

本专题关联

待沉淀

  • 计费场景:星型模型 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)。批处理和流处理是两种 计算引擎,不是两种数据。

专题文档(推荐先读全景串讲)

原始数据(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 分钟)

  1. 从文件 最上面 往下读,看到 ## 框架总览,读完那棵 ASCII 树和「一句话」
  2. 先不要 读后面的「第一篇 / 第二篇 / 第三篇」长文(那是以后慢慢补的)
  3. 往下拉## 组件 × 框架对照表,读完那张 7 行表(DDIA 概念 ↔ ClickHouse/dbt/Airflow/Flink)
  4. 拿一张纸,画三个框:基础分布式派生(批/流),旁边写上你熟悉的组件名

  5. 读完「框架总览」

  6. 读完「组件对照表」
  7. 画完三大篇草图
  8. :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 分钟(必做)

  • 待完善索引勾 3 条 下周要写的笔记
  • 30 秒 口述:三大篇各一句(可对着 小结 表)
  • 收工:今天不补读「刻意不读」的章

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)

关联§ 「有序」指什么 · § B+Tree

我的理解(错 / 不完整)

  • 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 是流这一路的主引擎

程序 = 数据结构 + 算法(算法面试)在数据工程侧的镜像:数据系统 = 存储模型 + 派生管道(批/流)