文章目录
数据复制
目的
- 高可用。容忍单个副本故障、网络分区。
- 可扩展。多副本提高读吞吐量。
- 低延时。距用户较近,交互更快。
主从复制
- 写:主节点。读:从节点。
- 到主节点网络问题影响所有写入。
复制形式
- 同步复制。从节点确认写入后返回。
- 从节点数据最新版本,可瞬间故障切换。
- 异步复制。从节点无需确认。
- 从节点不会影响主节点吞吐量。
- 半同步复制。一个从节点同步,其余异步。
复制日志
- 基于语句。INSERT等。
- 非确定性语句,now等执行结果不同。
- 依赖现有数据的操作,如UPDATE,需要保证副本按照相同顺序执行。
- 基于物理日志WAL,如redo。
- 和存储引擎耦合,偏底层。如果存储格式不前后兼容,无法滚动升级。
- 基于逻辑日志,如binlog。
- 复制和存储逻辑解耦。向后兼容,方便异构数据。
- 包含行前后值。记录因果关系,处理并发。
- 基于触发器。
- 灵活但是增加数据库压力。
运维变更
- 新增从节点。
- 一致性快照 -> 复制日志追赶变更。
- 从节点失效。根据复制日志追赶式恢复。
- 主节点失效。节点切换。
- 确认主节点失效。如心跳超时。
- 选新的主节点。选举->共识算法推送,节点和主节点差异小优先。
- 新主节点生效。写请求转发,原主节点恢复后降级成从节点。
- 节点切换风险。
- 脑裂。老主节点上线未失效,写冲突。
- 数据丢失。异步复制从节点落后,如果外部依赖这部分数据,危险。
- 使用自增主键,外部映射到该主键关系错误。
- 频繁切换。超时时间选取,可使用手工切换。
副本一致性
- 读写一致。从节点读到自己主节点写的数据。
- 从主节点读取用户可能修改的数据。
- 读取时带时间戳(版本号或时钟),副本需要覆盖写入的时间戳,否则重选副本。
- 单调读一致。读到新版本后不再读到旧版本。
- 根据用户id路由指定副本。
- 因果一致。先发生先读。
- 有因果关系的写入从一个副本读。
- happens-before追踪因果。
多主复制
适用场景
- 多数据中心。
- 性能。就近写,不用跨广域网。
- 容错。切换数据中心。
- 数据中心失效。
- 到数据中心的网络失效。
- 离线客户端。等价于单节点数据中心+极不可靠的网络。
- 协作编辑。
处理写冲突
异步检查:主节点各自写入的优势。
同步检查:简单。
- 避免冲突。用户路由到单节点写,但漫游或网络故障可能重新路由产生冲突。
- 收敛于一致。
- 最后写入者获胜(LWW)。
- 保留写入版本号最大记录。
- 保留节点版本号最大记录。
- 合并并发写入,依赖应用层(自动)、用户(手动)解决冲突。
- 版本号标记操作的因果关系。
- 最后写入者获胜(LWW)。
无主复制
- 用户写入多个节点并从多个节点读取。
- 通过数据版本号确认最新的有效值。
quorum一致
- 读写quorum。n个节点,w个写入确认,r个读取,w+r>n保证读写节点有重合,可以读到最新值。
- 读修复。读取到旧版本回写新版本。
- 反熵。后台进程check更新数据版本。
quorum一致局限
部分成功、复制滞后导致。
- 读写并发。写操作部分完成,读取不确定。
- 部分写失效。写操作成功数<w,操作失败但是能读取新值。
- 宽松quorum条件无法保证读写节点有重合。
- 网络分区导致可达节点<w个,临时节点暂存数据,保证总共写入w个,网络问题解决后数据回传。
数据分区
- 目的:提高扩展性,大数据集分散在多个节点上,分担查询负载。
- partition同义词。
- shard:ES,MongoDB
- region:Hbase
- tablet:Bigtable
- vnode:Cassandra
分区方式
- 基于关键字分区。
- 区间查询特性良好。每个分区可以关键字顺序查找。
- 基于关键字hash分区。
- 数据均匀分布。特定模式会数据倾斜,造成热点。
- 折中。如Cassandra。
- 复合主键第一列可hash分区,其他列用作SSTable的排序。
- 第一列分布均匀,其他列支持区间查找。
一致性哈希:特殊的动态分区方式。
- 适合缓存不适合持久化存储。
- 分区再平衡涉及节点少。
二级索引
- 基于文档。利于写,ES、Cassandra。
- 本地索引。每个分区独立,维护自己的二级索引。
- 并行查询,读延迟放大。
- 基于词条。利于读。
- 分区的全局索引。
- 单个文档更新,如果涉及多个不同分区的二级索引,写放大显著,通常异步。
分区再平衡
- 原因:
- 查询压力增加,需要更多CPU。
- 数据规模增加,需要更多硬盘。
- 节点故障,需要其他节点接管。
- 目标:
- 负载、存储在集群中分布均匀。
- 平衡过程中正常提供读写服务。
- 避免不必要的迁移,加快过程减少网络磁盘I/O影响。
- 触发方式:
- 自动。节点负载过重可能被认为失效,再平衡会加重负载,可能雪崩。
- 手动。
取模
mysql分库分表
- 迁移过程中数据在节点间复制,需要双写或者复制停写,读写受影响。
- 再平衡成本高。
- 节点N发生变化,每个节点都有数据迁移。
- 节点N发生多次变化,同一条数据多次迁移。
固定数量分区
ES
- 初始创建远超节点数的分区,节点->分区一对多。
- 新增节点时只需要调整分区、节点映射关系。
- 不影响节点的读写。
- 成本低。
动态分区
Hbase
- 初始预分区,或者集中在一个分区,节点->分区一对多。
- 分区数据量大于阈值则分裂,小于阈值则合并。
按节点比例分区
Cassandra
- 初始时每个节点具有固定数量的分区。Cassandra默认1节点->256个分区。
- 新增节点加入时,随机选择现有分区分裂拿走一半数据。
请求路由
- 客户端连接任意节点,节点转发到目标节点。
- gossip协议同步集群状态,如Cassandra。
- 客户端连接路由层,路由层转发到目标节点。
- zk同步集群状态,如hbase、codis。
- 客户端感知分区节点,直连目标节点。
- 样例:redis的三种集群方案
事务
- ACID
- 事务隔离级别
- 重试中止事务的问题:
- 超时认为已成功的事务未成功,重试需要保证幂等。
- 系统负载导致错误,重试更糟,需要重试上限、指数回退。
- 永久系统故障导致,重试无意义。
- 重试有除DB外的操作,如短信,需要分布式事务。
- 重试失败,需要补偿。
分布式系统问题
- 故障->部分失效。
不可靠网络
- client->network->server->network->client,都有可能出问题。
- 超时是检测故障唯一可行的方法。
- 网络采用分组传输,动态分区。是可变性和可靠性的权衡。
- 优点是可以用较高的资源利用率应对突发流量。
- 缺点是数据可能包拥堵排队,造成延迟的波动,这种异步网络的延迟是没有上界的。
不可靠时钟
分类
- 墙上时钟。绝对时钟,可以和NTP同步。
- 如
System.currentTimeMillis()
从1970.1.1开始的毫秒,不含闰秒。 - 同步时如果本地时钟远快于NTP服务器,会直接回拨本地时钟。
- 如
- 单调时钟。相对时钟,可以测量时间间隔(超时),NTP不同步。
- 如
System.nanoTime
。
- 如
依赖时钟的风险
- 依赖绝对时间戳,时间不同步,可能产生因果倒序。优先考虑基于递增计数器、时钟置信区间。
- LWW前因覆盖后果。
- LWW前因覆盖后果。
- 依赖相对时间戳,进程暂停,产生冲突。
- 根据时间校验租约->暂停->处理。可能暂停完租约过期,和其他节点处理冲突。
- 可以暂停前通知其他节点、大对象不回收定期重启等应对进程暂停问题。
- 根据时间校验租约->暂停->处理。可能暂停完租约过期,和其他节点处理冲突。
不可靠信息
真相由多数节点决定,节点不能根据自己的信息判断自身状态。//TODO zk故障全公司服务重启
- 无意误操作。如进程暂停,进程误以为自己还是Master。
- 应对:Fencing令牌,拒绝旧令牌的操作。
- 应对:Fencing令牌,拒绝旧令牌的操作。
- 拜占庭故障。如软件bug、恶意攻击。
- 应对:鉴权、加密等。
抽象模型
- 计时模型。网络是否有上界延迟。
- 同步模型。
- 部分同步模型。
- 异步模型。
- 节点失效模型。
- 崩溃终止
- 崩溃恢复。
- 拜占庭失效。
普遍情况:部分同步+崩溃恢复。
一致性与共识
- 事务隔离:处理事务并发时的各种临界条件。
- 分布式一致性:针对延迟、故障等协调副本状态。
可线性化
- 最强一致性模型。
- 基本思想:一个系统看起来只有一个副本,所有操作都是原子的。
- 使用场景:
- 加锁和主节点选举。
- 唯一性约束。主键、用户名等。
- 跨通道的时间依赖。消息消费在图片存储完成之后。
- 实现方式。考虑容错必须多冗余,涉及复制,只有共识算法可靠。
- 共识算法。VSR,Paxos,Raft,Zab
- 主从复制。从唯一节点读取。
- 使用快照隔离、脑裂则非线性化。
- 多主复制。非线性化。
- 无主复制。dynamo风格。
- 如果网络延迟,在写确认之前读取,仍然非线性化。
- 如果网络延迟,在写确认之前读取,仍然非线性化。
顺序保证
- 因果一致性弱于可线性化。偏序关系。
- 因果关系可以被排序(happens-before)
- 并发关系无法被排序
- Lamport时间戳。
- 唯一:计数器-存储节点ID的键值对
- 因果一致:节点追踪并维护接触的最大计数器,请求中带上该计数器。
事务与共识
- 共识的等价问题:
- 线性化的CAS寄存器
- 原子事务提交
- 全序广播
- 锁与租约
- 成员协调
- 唯一性约束
- 解决方式
- 单个主节点。
- 主节点失效:等待恢复、认为介入、共识算法选新主节点。
- 共识算法的要求:
- 安全性:
- 协商一致性。所有节点接收相同的决议。
- 诚实性。所有节点只能做一次决定。
- 合法性。结论一定是某个节点提出的。
- 活性:
- 可终止性。少于一半节点崩溃可达成结论。
- 安全性: