- 文档
- 教程
【免费下载链接】ddia
《Designing Data-Intensive Application》DDIA 第一版 / 第二版 中文翻译
本篇导读基于《Designing Data-Intensive Applications》(DDIA)中文翻译仓库中的 第一部分总览页 展开,系统梳理第一部分的五章内容——数据系统架构权衡、非功能性需求、数据模型与查询语言、存储与检索、编码与演化——并结合仓库中对应章节的源码级细节,帮助你建立从单机到分布式、从存储引擎到数据流模式的完整知识骨架。读完本文,你将掌握每一章的核心概念、关键术语与可直接定位的阅读路径,为后续阅读第二、第三部分打下基础。
第一部分在全书中的位置与阅读顺序
《Designing Data-Intensive Applications》共分三大部分:第一部分"数据系统基础"(Part I)、第二部分"分布式数据"(Part II)、第三部分"派生数据"(Part III)。正如 part-i.md 开篇所强调的:
本书前五章介绍了数据系统底层的基础概念,无论是在单台机器上执行的单点数据系统,还是分布在多台机器上的分布式数据系统都适用。
也就是说,第一部分的五章讨论的是所有数据系统共享的底层问题:如何权衡架构、如何定义需求、如何选择数据模型、数据如何存储与检索、数据如何编码与演化。这些内容不依赖"分布式"这一前提,因此是后续第二、第三部分的技术前提。
从仓库的 Hugo 内容结构也能印证这一点:content/tw/part-i.md的 front matter 中book_kind: part、book_number: I、weight: 100,而五章分别以weight: 101–105紧随其后(见 content/tw 目录),即按 1→2→3→4→5 的固定顺序组织,说明第一部分内部存在明确的前后依赖关系——例如 第三章 介绍的数据模型会被 第四章 的存储引擎设计复用,第五章 的编码演化问题又建立在前两章的术语之上。
第一章:数据系统架构中的权衡
第一章 回答一个根本问题:既然数据系统没有"唯一正确"的架构,我们应该如何在相互冲突的目标之间做取舍?章节开篇引用了 Thomas Sowell 的话:"没有解决方案,只有权衡取舍。"
本章首先区分两类应用:数据密集型(data-intensive)应用关心的是如何存储和处理海量数据、如何管理数据变更、如何在故障与并发面前保证一致性、如何维持高可用;而计算密集型(compute-intensive)系统的难点在于把某项庞大计算并行化。绝大多数现代 Web、移动、SaaS 与云应用都属于前者。
数据系统的标准构件
章节列出了几乎所有数据密集型应用都会用到的标准构件:
- 数据库(database):存储数据,供自己或其他应用日后查找
- 缓存(cache):记住开销昂贵的操作结果,加快读取
- 搜索索引(search index):允许用户按关键字搜索或过滤数据
- 流处理(stream processing):在事件和数据变更发生后立即处理
- 批处理(batch processing):定期处理累积的大批量数据
应用通常用若干软件系统(数据库、API 等)拼装而成;难点在于判断哪种工具最适合当前任务,以及当单件工具无法独立完成时如何组合它们。
三组核心权衡
章节正文围绕三组相对概念展开(对应 分析型与事务型系统、云服务与自托管、分布式与单节点系统 三个小节):
- 事务型系统(OLTP)与 分析型系统(OLAP):事务型系统由创建数据的后端服务组成,按某个键做低延迟的点查询(point query)并插入、更新、删除记录;分析型系统则存储事务型数据的只读副本,扫描海量记录计算聚合统计量。章节进一步讨论了数据仓库(data warehouse)、从数据仓库到数据湖(data lake)、权威记录系统与衍生数据(derived data)等概念,并引入了数据工程师与分析工程师两个专业角色。
- 云服务与自托管:对比二者的利弊(云服务的利弊),讨论云原生系统架构(云服务分层、存储与计算分离)以及云时代的运维模式。
- 分布式与单节点系统:说明何时应当从单节点转向分布式(分布式系统的问题),并延伸到微服务与无服务器(serverless)架构(微服务与无服务器)。
此外,本章还讨论了 数据系统、法律与社会 这一面向合规与用户权利的议题,并提供全书通用的术语基础——例如"前端与后端""数据基础设施""无状态服务"等概念。章节末尾的 总结 浓缩了上述权衡的核心结论。
第二章:定义非功能性需求
第二章 讨论功能需求之外的另一半:性能、可靠性、可伸缩性、可维护性等非功能性需求(nonfunctional requirements)。它们未必被明确写下来,但"一个慢得让人无法忍受、或是很不可靠的应用,几乎等于不存在"。
案例研究:社交网络首页时间线
为了让抽象概念落地,章节用一个类似 X(原 Twitter)的社交网络作为贯穿案例,并给出了真实的 SQL 查询示例——获取某位用户首页时间线的核心查询:
SELECT posts.*, users.* FROM posts JOIN follows ON posts.sender_id = follows.followee_id JOIN users ON posts.sender_id = users.id WHERE follows.follower_id = current_user ORDER BY posts.timestamp DESC LIMIT 1000由此引出两个关键工程概念(见 案例研究:社交网络首页时间线 与 时间线的物化与更新):
- 扇出(fan-out):一次发帖引发多个下游请求,请求数量被放大的倍数。
- 物化(materialization):预先计算并持续更新查询结果;时间线缓存就是一种物化视图(materialized view)。物化以写入时更多工作换取读取时更快的速度,章节还讨论了关注极多账号、或拥有海量粉丝的名人账号等极端场景下的取舍。
描述性能
描述性能 小节区分了延迟(latency)与响应时间(response time),并介绍了用平均值、中位数与分位数(尤其尾部延迟 p95/p99)来刻画用户体验的方法(平均值、中位数与分位数),以及如何将其落实为 SLO / SLA 指标(响应时间指标的应用)。
可靠性、可伸缩性与可维护性
- 可靠性(reliability):系统在出问题时仍能继续正确工作。章节覆盖容错(fault tolerance)、硬件与软件故障(含通过冗余容忍硬件故障)、以及人为因素——人为错误往往比硬件故障更常见(人类与可靠性)。
- 可伸缩性(scalability):系统负载增长时能否高效增加计算能力。描述负载 给出了描述负载的指标(如 Twitter 案例中的每秒发帖数、时间线读取率);共享内存、共享磁盘与无共享架构 对比了三种横向扩展的硬件组织方式。
- 可维护性(maintainability):从三个维度展开——可运维性(让运维更轻松)、简单性(管理复杂度)、可演化性(让变化更容易)。
第三章:数据模型与查询语言
第三章 讨论数据模型——"它们不仅影响软件的编写方式,还影响我们的解题思路"。章节用一个四层模型说明数据抽象的层次:应用层的数据结构与 API → 通用数据模型(JSON/XML 文档、关系表、图的顶点与边)→ 存储引擎的字节表示 → 硬件层。每层通过明确的模型隐藏下层复杂性。
关系模型与文档模型
本章对比的核心是关系模型(relational model,1970 年由 Edgar Codd 提出)与文件模型(document model,由 NoSQL 运动推广,常以 JSON 表示):
- 对象关系不匹配(阻抗不匹配):对象式编程语言与关系表模型之间需要笨拙的转换层。章节专门用一节讨论 ORM(如 ActiveRecord、Hibernate)的局限与优势(对象关系映射 ORM),包括著名的N+1 查询问题。
- 一对多关系:文档模型天然适合一对多关系(用于一对多关系的文档数据模型)。
- 规范化、反规范化与连接:讨论正规格的权衡,以及社交网络案例中的反规范化(正规格化的权衡、社交网络案例研究中的反规范化)。
- 多对一与多对多关系(多对一与多对多关系):文档模型在这一场景下需要仿照关系模型做连接。
- 星型与雪花型:分析模式(星型与雪花型:分析模式):分析型数据建模的经典模式。
- 何时使用哪种模型:从模式灵活性(读时模式 vs 写时模式)、读写的数据局部性、文档的查询语言、文档与关系数据库的融合四个角度给出决策框架(文档模型中的模式灵活性、读写的数据局部性、文档的查询语言、文档和关系数据库的融合)。
图数据模型、事件溯源与其他模型
- 图数据模型:从属性图(property graph,属性图)讲起,依次对比 Cypher 查询语言、SQL 中的图查询、三元组存储与 SPARQL(含 RDF 数据模型)、Datalog(递归关系查询)以及 GraphQL(Cypher 查询语言、SQL 中的图查询、三元组存储与 SPARQL、GraphQL)。
- 事件溯源与 CQRS(事件溯源与 CQRS):以不可变事件日志为核心的数据模型。
- 数据框、矩阵与数组(数据框、矩阵与数组):面向数据科学/机器学习场景的模型。
章节还以术语框形式介绍了声明式查询语言(SQL、Cypher、SPARQL、Datalog 等)与命令式算法的区别:声明式查询由查询优化器决定执行计划,数据库可以无修改地并行执行并提升性能。
第四章:存储与检索
第四章 从数据库的视角回答"数据如何存储、如何重新找到"。章节开篇用两个 Bash 函数展示了"世界上最简单的数据库",完整演示了追加写日志的核心思想:
db_set () { echo "$1,$2" >> database } db_get () { grep "^$1," database | sed -e "s/^$1,//" | tail -n 1 }调用db_set key value将键值对追加到文件末尾,db_get key返回该键最后一次出现的值。这个例子说明:仅追加(append-only)日志是所有复杂存储引擎的基础,真正的数据库还要解决并发写入、磁盘空间回收、崩溃恢复(处理写了一半的记录)等问题。
OLTP 存储引擎的两大流派
- 日志结构(log-structured)存储:以不可变文件顺序写入数据,通过压实(compaction)回收空间。章节依次讲解SSTable 文件格式、SSTable 的构建与合并、布隆过滤器(加速键是否存在判断)与压实策略(SSTable 文件格式、构建和合并 SSTable、布隆过滤器、压实策略)。
- B 树(B-tree):就地更新数据页的经典结构,需要*预写日志(WAL)*保证可靠性(使 B 树可靠),并有多种变体。
随后章节给出 B 树与 LSM 树的系统性对比(比较 B 树与 LSM 树):读取性能、顺序与随机写入、写放大(write amplification)、磁盘空间使用四个维度。此外还覆盖多列索引与二级索引、全内存存储(in-memory)等主题。
分析型数据存储与高级索引
- 分析型数据存储(分析型数据存储):云数据仓库、列式存储(列压缩、排序顺序、写入列式存储),以及现代查询引擎的编译与向量化执行(查询执行:编译与向量化)、物化视图与多维数据集。
- 多维度索引与全文索引(多维度索引与全文索引):全文检索与向量嵌入(embedding,向量嵌入)等支撑复杂查询的索引技术。
第五章:编码与演化
第五章 的主题是"唯变所适":应用必然随时间变化,数据格式与模式也随之演化。章节的核心框架是一对兼容性定义:
- 向后兼容(backward compatibility):较新的代码可以读取由较旧代码写入的数据——通常不难实现。
- 向前兼容(forward compatibility):较旧的代码可以读取由较新代码写入的数据——更难,因为旧代码必须忽略新代码新增的字段。
章节还用一个具体场景(旧版代码读出新字段记录、更新后写回,若模型对象不显式保留未知字段就会丢失数据)说明向前兼容的难点,这也是选择编码格式时最重要的判据。
编码数据的格式
- 特定语言的格式(如 Java
Serializable、Pythonpickle、RubyMarshal):方便但存在语言锁定、任意类实例化的安全风险、版本管理缺失、效率低下等问题,通常不适合长期存储。 - JSON、XML 及其二进制变体:跨语言标准格式,章节讨论了JSON Schema与二进制编码方案。
- Protocol Buffers:通过字段标签(field tag)支持模式演化(字段标签与模式演化)。
- Avro:基于写入者模式与读取者模式的兼容机制(写入者模式与读取者模式),以及模式演化规则、动态生成的模式等特性。
- 模式的优点(模式的优点):显式模式带来的文档价值与校验能力。
数据流的模式
编码不仅用于存储,也用于进程间通信。章节将数据流归纳为三种模式:
- 流经数据库的数据流:不同时间写入的不同值、归档存储(不同时间写入的不同值、归档存储)。
- 流经服务的数据流:REST 与 RPC:Web 服务、RPC 的固有问题(重试、超时、幂等)、负载均衡器、服务发现与服务网格,以及 RPC 场景下的数据编码与演化(Web 服务、RPC 的问题、负载均衡器、服务发现和服务网格、RPC 的数据编码与演化)。
- 事件驱动的架构:持久化执行与工作流(durable execution)、消息代理(message broker)与分布式 actor 框架(持久化执行、消息代理、分布式 actor 框架)。
仓库中的阅读路线与延伸资源
本仓库同时维护了简体中文、繁体中文与第一版译文,便于对照阅读:
- 简体中文第一部分的五章见 content/zh/ch1.md 至 content/zh/ch5.md;本篇所依据的繁体中文版见 content/tw/ch1.md 至 content/tw/ch5.md;
- 第一版译文位于 content/v1 目录,可供版本对比;
- 每章的图集中存放于 static/fig(如 ddia_0201.png、ddia_0501.png 等按章节编号),章首还配有位于 static/map 的章节思维导图(如本章引用的 ch01.png 对应第二章);
- 全书目录与阅读说明见 README.md,其"目录"一节列出了三大部分共十四章的完整结构。
读完全部五章之后,即可进入 第二部分(分布式数据,对应第六章至第十章),届时第一部分的单机知识会与复制、分片、事务、一致性与共识等分布式主题衔接起来。从仓库的 front matter 结构看,第二部分以book_number: II、weight紧随第一部分编排(见 content/tw/part-ii.md),两部分构成全书递进的知识链条。
结语
第一部分"数据系统基础"是整本 DDIA 的基石:第一章确立"权衡"的世界观,第二章给出衡量系统的指标语言,第三章教会你选择数据模型,第四章揭示存储引擎的内部机理,第五章则解决系统长期演化中的兼容性问题。五章环环相扣、层层递进,无论你最终面向单机系统还是分布式系统,这部分知识都同样适用。建议按照 ch1 → ch5 的顺序通读,并结合本仓库的章节链接与思维导图资源边读边对照,为第二部分的分布式主题做好准备。
- 文档
- 教程
【免费下载链接】ddia
《Designing Data-Intensive Application》DDIA 第一版 / 第二版 中文翻译
相关推荐
BepInEx 6.0.0:插件加载稳定性机制拆解与IL2CPP调优实操手册
BepInEx 6.0.0:插件加载稳定性机制拆解与IL2CPP调优实操手册 BepInEx 6.0.0 是面向 Unity 与 .NET 游戏的插件框架。本篇
文档教程DDIA 第三部分导读:派生数据(Derived Data)与多系统数据集成架构
DDIA 第三部分导读:派生数据(Derived Data)与多系统数据集成架构 《Designing Data Intensive Application》(
文档教程DDIA核心概念解析:数据系统基础架构
DDIA核心概念解析:数据系统基础架构 本文深入探讨了现代数据系统的基础架构设计,重点分析了事务型系统(OLTP)与分析型系统(OLAP)的核心区别、技术实现差
文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考