加载中...
数据密集型应用系统设计

DDIA by Martin Kleppmann

数据密集型应用系统设计

存储与检索

  1. 一个极简数据库道尽了联机事务处理的本质:存储与检索
  2. 日志结构存储的核心流程是什么?MemTable、SSTable、LSM、布隆过滤器、合并压实
  3. 什么是稀疏索引?只存储部分键的索引,不保存所有键,给有序键分组,存每组的第一个,
  4. 什么是二级索引、聚簇索引、覆盖索引?
    1. 关系数据库中用 CREATE INDEX 创建的就是二级索引,二级索引的索引值不一定唯一,InnoDB 二级索引的值存的是相关行的主键,Postgres 存的是磁盘位置的引用(堆文件)。
    2. 如果实际数据(行、文档、顶点)直接存储在索引中,那么就是聚簇索引,mysql InnoDB 主键索引就是聚簇索引。
    3. 两者的折中是覆盖索引,或称包含列的索引,在索引中存储了某些列,这就允许直接用索引回答查询而无需解析主键或查看堆文件。
  5. 什么是全内存存储?
    1. 内存数据库避免了将内存数据结构编码为写入磁盘的形式的开销,毕竟磁盘数据库也能把数据缓存在内存中。
    2. 一些内存数据库对于重启机器丢失数据是接受的如 Memcached,另一些内存数据库旨在实现持久性,通过电池供电的 RAM、更改日志写入磁盘、定期快照写入磁盘、复制内存状态到其他机器等方式来实现
带有稀疏索引的 SSTable,允许查询跳转到正确的块
合并多个 SSTable 段,仅保留每个键的最新值,类似 归并排序 算法,删除一个键及其关联的值必须向数据文件追加一个称为 墓碑 ( tombstone )的特殊删除记录
布隆过滤器提供了一种快速的概率检查,用于判断特定键是否存在于特定 SSTable 中
OLAP 中按列而不是按行存储关系数据
存储与检索
加载中...

复制

  1. 复制为了什么?故障仍可用、地理上接近用户、读副本增加吞吐量
  2. 复制与备份的区别是什么?备份是存旧快照可回滚,复制是数据的实时同步
  3. 复制的核心算法主要有哪三种?
    1. 单主复制:一个副本是主节点,其余的是从节点;写入发送给主节点,其余节点通过主节点发送的复制日志应用所有写入,mysql 中的 binlog 逻辑日志,raft 基于单主节点;
      1. 什么是同步复制、异步复制?同步复制是主节点等从节点确认,异步复制是主节点发送变更后直接让客户端报告成功
      2. 不停机设置新副本的核心流程是什么?先从主节点复制快照,然后追上快照拍摄后的写入
      3. 节点故障怎么处理?主节点故障:故障转移;从节点故障:追赶恢复
      4. 复制日志如何实现?基于语句、基于预写日志、基于逻辑日志(基于行)
    2. 多主复制:多个节点同时接收写入,也同时是其他主节点的从节点,特别适合跨地域的地区之间主节点互相复制、本地优先的一些软件
      1. 多主复制有哪些拓扑结构?双主节点拓扑、环形拓扑、星形拓扑、全对全拓扑
      2. 写入冲突时如何处理?不同主节点上的并发写入,并发定义为写入发生时,两个写入互相不知道对方的存在
        1. 最后写入者胜 LWW:丢弃并发写入,本质是随机选一个写入
        2. 手动冲突解决:数据库存所有写入值为兄弟节点,查询时返回所有,让程序自动或用户手动合并,最后写回新值解决冲突
        3. 自动冲突解决:操作变换、无冲突复制数据类型
    3. 无主复制:放弃主节点概念,任何副本都能接受写入
      1. 写入时并行发送到 n 个副本,收到 w 个节点的确认就认为写入成功;读取时并行发送到 n 个副本,等待r个节点的响应,通过版本号/时间戳区分最新值
      2. 读写仲裁:当w + r > n时,读取的r个节点与写入的w个节点必然存在重叠,可保证读取到最新值
  4. 复制延迟的问题:单主复制+异步复制+读扩展
    1. 最终一致性:异步从节点落后时客户端读到过时信息,停止写入并等待后最终从节点会赶上主节点,达成数据一致;这种复制延迟在正常操作时为几分之一秒,系统高负载或网络故障时会长达几秒甚至几分钟。
    2. 复制延迟引起的三大核心异常
      1. 读己之写(写后读):用户写入后立即查看,异步从节点尚未同步,用户看不到提交的数据 —> 1min 内读主节点/同步副节点;客户端记录最近写入的时间戳;—> 用户始终能看到自己提交的更新,但不对其他用户的更新可见性做承诺
      2. 单调读:用户多次读取路由到不同副本,先看到最新数据后看到过时数据,发生时间倒退 —> 确保多次读同一个副本 —> 用户多次读不会看到时间倒退
      3. 一致前缀读:分片数据库中,因果相关的写入,不同分片复制延迟不同,用户先看到结果后看到原因 —> 确保因果相关的写入都写入同一个分片 —> 按特定顺序发生的写入,在读取时也是相同的顺序,不会因果倒置
  5. 并发写入检测与版本控制:多主、无主复制时并发写入导致副本不一致
    1. 这里的并发与物理时间有关系吗?与物理时间是否重叠无关,核心是信息是否可达。
    2. 通过算法判断两个操作是否并发;先发生的操作应覆盖之前的操作,并发操作需触发冲突解决。
复制
加载中...

分片

  1. 分片是什么?单机无法承载,不一定要在每个节点保存所有数据,可以将大量数据划分为更小的分片(分区),不同的分片存在不同的节点上,优势在于可水平扩展、负载并行,劣势在于复杂、关系数据处理难、事务问题。
  2. 键值数据分片的常见策略有哪些?
    1. 范围分片:按 ID、时间、区域等连续范围切分,但易热点、数据倾斜
    2. 哈希分片:对 key 做 hash 后取模,但扩缩容要重新 hash 大量数据迁移
      1. 一致性哈希:虚拟节点解决倾斜,节点变化迁移量最小
  3. 读写时如何定位节点?请求路由问题:
    1. 客户端直连任何节点,请求转发
    2. 路由层负载均衡器转发
    3. 客户端直连确定节点,客户端知晓分片在节点上的分配
  4. 二级索引怎么办?
    1. 本地二级索引:每个分片只索引其自己分片内的记录,读取时需要分散查询,例如 ES
    2. 全局二级索引:索引覆盖全部分片,索引自身也会分片,写入时需要涉及多个分片,同步复杂,例如 TiDB
将请求路由到正确节点的三种不同方式
使用 ZooKeeper 跟踪分片到节点的分配
本地二级索引:每个分片只索引其自己分片内的记录
全局二级索引反映来自所有分片的数据,并且本身按索引值进行分片
分片
加载中...

事务

事务
加载中...

批处理

批处理
加载中...

流处理

流处理
加载中...

历史记录

共 7 条
  1. 2026-03-05 17:15:44
    init
  2. 2026-03-24 17:24:30
    增加了《复制》一章
  3. 2026-03-25 15:09:50
    增加了《分片》一章
  4. 2026-03-26 17:13:03
    增加了《存储与检索》一章
  5. 2026-03-27 18:16:30
    增加了《事务》一章
  6. 2026-03-29 18:50:14
    《事务》一章增加了一些问答
  7. 2026-03-29 20:51:01
    增加了《流处理》、《批处理》共两章