《数据密集型应用系统设计》笔记二
《数据密集型应用系统设计》笔记二
第五章:数据复制
复制:通过网络在多台机器上保存相同数据副本。目的:
- 降低访问延迟(如 CDN)
- 提高可用性(部分故障时仍可工作)
- 提高读吞吐量(扩展读能力)
主流复制模式:主从复制、多主复制、无主复制。
主节点与从节点
主从复制工作流程:
- 指定一个副本为主副本,接受写请求并写入本地存储
- 主副本将数据变更发送给所有从副本,保持相同写入顺序
- 从副本为只读,客户端可在任意副本读取

支持主从复制的系统:MySQL、PostgreSQL、MongoDB、Redis、Kafka、RabbitMQ 等。
同步复制与异步复制

- 同步复制:优点——数据强一致;缺点——任一同步节点中断则整个系统无法写入
- 异步复制:优点——吞吐性能好;缺点——主节点失败可能丢失未复制数据
- 推荐模式:一个或半数以上从节点同步成功即返回,其余异步同步
配置新的从节点
步骤:一致性快照 → 拷贝到从节点 → 请求快照后变更日志 → 追赶同步
处理节点失效
- 从节点失效:追赶式恢复(根据复制日志请求中断期间的变更)
- 主节点失效:自动切换(超时检测 → 选举新主节点 → 重新配置系统)
主节点切换的风险:
- 异步复制下未同步数据可能丢失
- 自增 ID 不一致导致外部系统引用冲突
- 脑裂:两个节点同时自认为主节点
- 超时设置过长恢复慢,过短导致不必要切换
复制日志的实现
- 基于语句的复制:转发 SQL 语句,存在非确定性函数、自增列等问题
- 基于 WAL 传输:底层字节序列日志,与存储引擎紧密耦合
- 基于行的逻辑日志复制:行级别描述写请求,与存储引擎解耦,向后兼容
- 基于触发器的复制:灵活性高但开销大,适合定制化场景
复制滞后问题
主从复制 + 读写分离是读密集负载的常见方案。异步复制下从节点可能滞后,导致最终一致性。
读自己的写
用户写入后立即读取,可能从滞后的从节点读到旧数据。解决方案:
- 用户自己修改的内容从主节点读取
- 跟踪最近更新时间,一分钟内在主节点读取
- 客户端携带时间戳,确保读取至少包含该时间戳的更新

单调读
确保用户依次读取时不会看到回滚现象(读到新值后又读到旧值)。实现:每个用户固定从同一副本读取。

前缀一致读
按因果顺序发生的写操作,读取时也按相同顺序。

复制滞后的解决方案
分布式事务。
多主节点复制
主从复制只有一个主节点,影响所有写入。多主复制适用场景:
- 多数据中心:每个数据中心内有常规主从复制,数据中心间各自主节点交换数据

- 离线客户端操作:每个设备充当本地领导者,异步同步(如笔记软件)
- 协作编辑:多用户同时编辑,需加锁避免冲突
多主 vs 主从差异:性能(本地快速响应)、容错(各数据中心独立运行)、网络容忍(异步复制更好)
处理写冲突
多主复制的最大问题是写冲突。
收敛策略:
- 最后写入者获胜(LWW):基于时间戳,可能丢数据
- 副本 ID 优先级排序
- 合并多个值
- 保留冲突信息,应用层事后解决
冲突处理时机:写入时(检测到冲突即调用处理程序)或读取时(返回多版本由应用层解决)
拓扑结构
复制拓扑描述写入传播路径:

- 全部到全部:容错性好,但网络问题可能导致消息乱序
- 环形/星形:单点故障可能中断消息流

解决消息乱序:版本向量技术。
无主复制
客户端向多个节点写,从多个节点并行读,检测和纠正过期数据。
第六章:分区
分区在不同系统中的称呼:MongoDB 的 shard、HBase 的 region、Bigtable 的 tablet、Cassandra 的 vnode。
分区目的:提高可扩展性,将数据和查询负载均匀分布在多节点上。
数据分区与数据复制
分区通常与复制结合:每个分区在多个节点有副本,一个节点可能同时是某些分区的主副本和其他分区的从副本。
键值数据的分区
倾斜:分区不均匀导致某些节点承担过多负载。热点:负载严重集中在某个分区。
基于关键字区间分区
每个分区负责一段连续关键字范围。优点:支持高效区间查询。缺点:某些访问模式导致热点(如时间戳关键字)。
基于关键字哈希值分区
哈希函数均匀分布数据,每个分区负责一段哈希值范围。优点:负载均匀。缺点:丧失区间查询能力。

负载倾斜与热点
- 基于哈希可减轻但无法完全避免热点
- 简单规避:在关键字末尾加随机数,分散到不同分区
分区与二级索引
基于文档分区的二级索引(本地索引)
每个分区独立维护自己的二级索引。优点:写入只需更新一个分区。缺点:读取需 scatter/gather 所有分区。

基于词条的二级索引分区(全局索引)
全局索引也需分区。优点:读取高效。缺点:写入慢且复杂,需更新多个分区。全局二级索引更新通常是异步的。

分区再均衡
再均衡需满足:负载更均匀、过程中数据库可正常服务、避免不必要的迁移。
动态再均衡策略
- 固定数量分区:创建远超节点数的分区,节点增减时迁移分区
- 动态分区:分区超过阈值则分裂(HBase 默认 10GB),过小则合并
- 按节点比例分区:分区数与节点数成正比(Cassandra、Ketama)
请求路由
三种路由策略:
- 客户端连接任意节点,不合适则转发
- 所有请求经过路由层(分区感知负载均衡器)
- 客户端直接感知分区和节点关系

ZooKeeper 常用于跟踪分区到节点的映射。Cassandra/Riak 使用 Gossip 协议。

小结
- 区间分区:支持高效区间查询,但存在热点风险,动态分裂
- 哈希分区:负载均匀,区间查询效率低,固定分区数迁移
二级索引分区:
- 文档分区索引:写入简单,读取需 scatter/gather
- 词条分区索引:读取高效,写入需更新多个分区
第七章:事务
事务中所有读写是一个执行整体:要么成功(提交),要么失败(中止/回滚)。
深入理解事务
ACID:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
原子性
事务被视为不可分割的最小单元,所有操作要么全部成功,要么全部回滚。
弱隔离级别
串行化
第八章:分布式系统的挑战
所有可能出错的事情一定会出错。
分布式系统的问题:
- 不可靠的网络:数据包可能丢失、延迟,无法确定消息是否发送成功
- 不可靠的时钟:可能突然跳跃或倒退
- 进程可能遭遇长度未知的暂停(如 GC),被宣告失效后又恢复
部分失效是分布式系统的关键特征。分布式算法依靠超时检测故障,但无法区分网络和节点故障。检测到错误后需多节点共识协议达成决策。
第九章:一致性与共识
分布式系统最重要的抽象之一:共识——所有节点就某项提议达成一致。
参考资料
- 数据密集型应用系统设计 - 这可能是目前最好的分布式存储书籍,强力推荐【进阶】