这是我参与「第五届青训营 」伴学笔记创作活动的第 11 天
今日笔记内容: 分布式理论-现代架构基石, 第二段
分布式事务
两阶段提交 (Two-phase Commit)
Prepare(写入) + Commit(提交)
假设:
- 引入协调者(Coordinator)和参与者(Participants)
- 所有节点都采用预写使日志, 且日志被写入后可以持久化在可靠的存储设备上
- 所有节点不会永久性损坏, 损坏后仍然可以恢复 问题:
- 性能问题: 需要多次节点间相互通信, 耗时过大, 资源需要被锁定
- 协调者单点故障: 如果事务协调者节点宕机, 需要另起新的协调者, 否则参与者陷入中间状态, 事务无法正常结束
- 网络分区带来数据不一致: 部分参与者收到Commit消息, 另一部分没有
三阶段提交
将两阶段提交中的Prepare阶段, 拆成两部分: CanCommit和PreCommit, 避免了commit时资源不足导致commit失败或阻塞的问题
关于分布式事务更详细的知乎文章: (分布式事务有这一篇就够了! - 知乎 (zhihu.com))
MVCC
多版本并发控制, 维持一个数据的多个版本使读写操作没有冲突, 不阻塞读写, 提升并发性能
版本通过时间戳区分
Spanner : 使用TrueTime API 提供物理时钟, 偏差在1~7ms之间
时间戳预言机(TSO), 利用中心化的授时方式, 所有协调者向中心化节点获取时钟
乐观锁与悲观锁
乐观锁: 不主动加锁, 更新数据时先判断是否已经有线程更新 悲观锁: 操作数据前先把数据锁住, 操作完成后解锁
共识协议
Quorum NWR模型
Quorum 法定人数
- N: 有多少节点
- W: 一次成功的更新操作需要至少w份数据
- R: 一次成功的读数据需要从R份拷贝中读取
为了保证强一致性, 要满足W+R>N
用户可以调整W,R来选择CAP特性
Raft 协议
基于Leader/Follower机制的共识协议, 客户端与Leader通信写入数据, Leader负责向Follower同步数据
- Log(日志), 节点之间存储的信息, 只允许追加写, 避免数据被覆盖的问题
- Term(任期号), 单调递增, 每个任期内最多有一个Leader
- Committed: 日志被复制到多数派后, 才可以被认为可以提交
- Applied: Leader Commited日志后, 将日志内容应用到状态机上
两大RPC:
- 选举
- 超时(随机时间)没有收到Leader心跳包, 则开启新任期, 尝试竞选, 向其他节点发送投票请求
- 收到多数派响应, 成为新任期Leader
- 响应没有达到多数派, 继续下一轮选举
- 收到其他投票请求, 比较任期号, 选择投票给自己还是其他节点
- 每个节点一个任期内只能投一次票
- 日志复制
- Leader收到客户端新日志后, 将日志复制到其他Follower上
- Leader保留所有Follower当前日志下标, 如果不同步则定时发送复制RPC
- 新日志成功复制到多数派节点后, 更新提交下标, 并通知其他Follower
- 其他Follower接收到提交下标后, 更新自己的状态机
Paxos
比Raft协议更早提出, 但更加复杂
- Multi-Paxos 可以并发修改日志, 而Raft的日志必须是连续的
- Multi-Paxos 可以随机选主, 不要求最新最全的节点当选Leader
- 优势: 所有节点都可以写入, 且不要求连续写, 因此性能更高
- 劣势: 不保证有节点保存所有状态, 恢复流程复杂, 需要同步历史记录
思考题
- 分布式系统有那些优势和挑战?
分布式的优势在于他可以理论上无限水平扩容, 也可以通过冗余机制来容灾. 挑战在于大规模分布式系统基础设施的不可靠性, 如何在集群间形成共识和有效的通信. - 两将军问题为什么理论上永远达不成共识?
每次重复通信只能增加消息可信的概率, 不能达到100%的可信 - 为什么TCP采用三次握手? 而不是两次和四次?
三次握手验证了双端的发送(seq),接收响应能力(ack) - 为什么在4将军问题中, 增加一轮协商就可以对抗拜占庭故障?
通过可信信息的冗余来覆盖不可信信息 - 什么是最终一致性? 什么是线性一致性?
最终一致性: 写入过程中, 不保证所有节点读取数据的一致性, 只保证在有限时间内, 能将数据更新到所有节点上 线性一致性: 写入过程中, 如果有节点读取到新数据, 则强制同步到所有其他老节点上, 保证在读取到新数据后, 不会再读取到老数据 - CAP理论中, 请举例说明可用性和一致性的矛盾?
在AP场景下, 网络产生分区, 且系统仍然提供服务, 则不同分区的服务无法满足一致性 在CP场景下, 网络产生分区, 必须停止服务, 避免后续对系统状态的更新, 保证系统的一致性 - ACID理论中的一致性和CAP理论中的一致性有什么区别?
CAP理论中的C注重多节点之间同一份数据的一致性, ACID中的C关注的是整体数据逻辑上的一致性 - 两阶段提交中, 什么场景需要数据库管理员介入?
两阶段提交中可能会出现预写入死锁, 或提交时机器宕机导致数据不一致, 此时需要管理员介入 - 三阶段提交缓和了两阶段提交的哪两个问题?
- 单点故障,
- 阻塞问题
- 什么场景适合乐观锁? 什么场景适合悲观锁?
乐观锁使用与数据竞争较小的场景, 通常是读频繁, 避免反复加锁. 悲观锁适用于更新频繁的场景, 需要互斥保证更新的安全性 - 在共识协议中, 为什么说允许数据被覆盖会带来数据一致性问题
同一份数据被更新后没有同步到所有节点上, 可能读取到老的数据 - RAFT协议中, Leader写成功日志Log20但未同步给Followers后发生宕机, Follower重新选举后产生一条新的日志Log20, 这时Leader重启, 整个系统发现两种不一样的Log20记录, 如何区分并拒绝掉前面的Log20?
Follower重新产生了一条新日志Log20, 此时Follower已经成为新Leader, 并尝试将新Log20记录同步给其他节点, 老Leader接收到新Leader的日志同步请求后, 降级为Follower, 并将自己原来的Log20更新为新的. - RAFT协议中, Stale读是如何产生的? 该如何解决Stale读的问题 旧Leader在响应没有同步的日志, 1. 只响应已提交的日志, 2. Leader重启后必须经过心跳包后才响应客户端, 可以通过心跳包判断是否有新Leader产生.