说明
底本为 Designing Data-Intensive Applications(Martin Kleppmann);版权归原作者与出版社;本文为个人学习笔记,转载请注明出处。
本文覆盖 第 1–4 章:可靠性与可扩展性、数据模型、存储引擎、数据编码。插图等资源位于站点 /images/(含 ddia-part1-* 与 ddia/part2、ddia/part3 等路径)。
系列总览与导航:《DDIA》读书笔记系列 · 仅目录页
第1章:可靠性、可扩展性与可维护性
1.1 数据密集型应用的组成
现代数据密集型应用通常由多个组件组合:
| 类型 | 特点 | 主要瓶颈 | 典型场景 |
|---|---|---|---|
| 数据密集型 | 数据量大、复杂、变化快 | 磁盘I/O、网络带宽 | Web应用、社交网络 |
| 计算密集型 | 计算量大、算法复杂 | CPU处理能力 | 科学计算、图形渲染 |
1.2 可靠性(Reliability)
可靠性:系统在面对故障时,仍能正确运行
| 术语 | 英文 | 定义 |
|---|---|---|
| 故障 | Fault | 系统中某个组件偏离其规格 |
| 失效 | Failure | 整个系统停止为用户提供服务 |
| 容错 | Fault-tolerant | 系统能够处理某些类型的故障 |
故障类型及应对
1. 硬件故障
- 硬盘损坏、内存故障、电源中断
- 应对:RAID、双电源、热备份、多副本
2. 软件错误
- 比硬件故障更难预测,可能导致系统性问题
- 应对:全面测试、进程隔离、监控告警
3. 人为错误
- 研究表明,运维人员的配置错误是系统中断的首要原因
- 应对:良好的API设计、沙箱环境、快速回滚
1.3 可扩展性(Scalability)
可扩展性:系统应对负载增长的能力
描述性能:百分位数
| 百分位 | 含义 | 用途 |
|---|---|---|
| p50 | 中位数 | 典型响应时间 |
| p95 | 95%的请求快于此值 | 较高标准 |
| p99 | 99%的请求快于此值 | SLA标准 |
Twitter 案例:推拉模式
| 方案 | 描述 | 优点 | 缺点 |
|---|---|---|---|
| 拉模式 | 读取时间线时查询关注者的推文 | 写入简单 | 读取开销大 |
| 推模式 | 发推时写入所有关注者的缓存 | 读取快速 | 写入开销大(大V问题) |
| 混合模式 | 普通用户推模式,大V拉模式 | 平衡读写 | 实现复杂 |
扩展策略
- 纵向扩展:使用更强大的机器(简单但有上限)
- 横向扩展:使用多台普通机器(需要复杂设计)
- 弹性扩展:根据负载自动增减资源
1.4 可维护性(Maintainability)
软件的大部分成本不在于最初的开发,而在于后续的维护
| 方面 | 目标 |
|---|---|
| 可操作性 | 让运维团队能够轻松保持系统运行 |
| 简单性 | 让新工程师能够轻松理解系统 |
| 可演化性 | 让工程师能够轻松对系统进行修改 |
第2章:数据模型与查询语言
2.1 数据模型分层
2.2 关系模型
| 概念 | 描述 | 示例 |
|---|---|---|
| 关系(Relation) | 表 | users表 |
| 元组(Tuple) | 行/记录 | 一个用户记录 |
| 属性(Attribute) | 列 | name, email, age |
1 | -- 规范化设计:使用外键引用 |
优势:数据一致性、灵活查询、ACID事务、成熟稳定
局限:对象-关系阻抗不匹配、模式僵化、扩展困难
2.3 文档模型
代表产品:MongoDB, CouchDB, Elasticsearch
1 | { |
| 特性 | 文档模型 | 关系模型 |
|---|---|---|
| 数据结构 | 嵌套/层次化 | 扁平/规范化 |
| 模式 | 灵活(Schema-on-read) | 严格(Schema-on-write) |
| 局部性 | 好(整个文档一起存储) | 差(需要JOIN多表) |
| 多对多关系 | 较难处理 | 容易处理 |
2.4 图模型
适用场景:社交网络、知识图谱、推荐系统
1 | // Neo4j Cypher 示例 |
2.5 数据模型选择指南
第3章:存储与检索
3.1 两大存储引擎家族
| 类型 | 优化目标 | 典型应用 | 代表产品 |
|---|---|---|---|
| 日志结构存储 | 写入优化 | 高写入负载 | LevelDB, RocksDB, Cassandra |
| 原地更新存储 | 读取优化 | 事务处理 | MySQL InnoDB, PostgreSQL |
3.2 哈希索引
局限:所有键必须在内存中、范围查询慢
3.3 LSM-Tree(Log-Structured Merge-Tree)
优化技术:
- 布隆过滤器:快速判断键是否存在(避免无效磁盘读取)
- 压缩策略:Size-Tiered(写密集)、Leveled(读密集)
3.4 B-Tree
| 特性 | B-Tree | LSM-Tree |
|---|---|---|
| 写入 | 原地更新 | 追加写入 |
| 读取 | 快(一次定位) | 可能需要检查多个文件 |
| 写放大 | 较低 | 较高(压缩开销) |
| 空间利用 | 可能碎片化 | 更紧凑 |
3.5 OLTP vs OLAP
| 特性 | OLTP | OLAP |
|---|---|---|
| 全称 | 在线事务处理 | 在线分析处理 |
| 主要操作 | 增删改查 | 复杂查询、聚合 |
| 访问模式 | 基于键的随机访问 | 扫描大量记录 |
| 数据量 | GB~TB | TB~PB |
| 响应时间 | 毫秒级 | 秒~分钟级 |
列式存储优势:
- 只读取需要的列
- 相同类型数据更易压缩
- 向量化处理
第4章:数据编码与演化
4.1 兼容性概念
滚动升级:不停机部署新版本,同时运行新旧版本代码
4.2 编码格式对比
| 特性 | JSON | Protobuf | Thrift | Avro |
|---|---|---|---|---|
| 可读性 | 高 | 无 | 无 | 无 |
| 空间效率 | 低 | 高 | 高 | 最高 |
| 模式 | 可选 | 必须 | 必须 | 必须 |
| 模式演化 | 手动 | 支持 | 支持 | 支持 |
| 动态模式 | 天然 | 困难 | 困难 | 支持 |
4.3 Protocol Buffers 示例
1 | message Person { |
兼容性规则:
| 操作 | 后向兼容 | 前向兼容 |
|---|---|---|
| 添加可选字段 | ✓ | ✓ |
| 删除可选字段 | ✓ | ✓ |
| 添加必填字段 | ✗ | ✗ |
| 修改字段标签 | ✗ | ✗ |
4.4 数据流模式
1. 数据库:数据可能比代码更持久,需要能够读取多年前写入的数据
2. 服务调用(REST/RPC):
- REST:基于HTTP,资源导向
- gRPC:基于Protobuf,高性能
3. 消息传递:
- 解耦、缓冲、异步、可靠性
- 代表:Kafka, RabbitMQ
本章关键要点
- 可靠性不是追求零故障,而是在故障发生时系统仍能正常工作
- 百分位数比平均值更能反映用户真实体验
- 抽象是管理复杂性的最重要工具
- 没有万能的数据模型,选择取决于应用场景
- LSM-Tree优化写入,B-Tree优化读取
- 兼容性是渐进式部署的前提
延伸阅读
- 《Site Reliability Engineering》:Google SRE 实践
- 《Database Internals》:数据库内部原理深入指南
- 《Building Microservices》:服务间通信详解