分布式理论 | 青训营笔记

111 阅读8分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天

分布式理论

概述

分布式系统是计算机程序的集合,这些程序利用跨多个独立计算节点的计算资源来实现共同的目标,可以分为分布式计算、分布式存储、分布式数据库等!

优势:

  • 去中心化
  • 低成本
  • 弹性
  • 资源共享
  • 可靠性高

挑战:

  • 普遍的节点故障
  • 不可靠的网络
  • 异构的机器与硬件环境
  • 安全

分布式存储

  1. Google File System(GFS),谷歌分布式文件系统
  2. Ceph:统一的分布式存储系统
  3. Hadoop HDFS:基于GFS架构的开源分布式文件系统
  4. Zookeeper:高可用的分布式数据管理与系统协调框架

分布式数据库

  1. Google Spanner:谷歌可扩展的、全球分布式的数据库
  2. TiDB:开源分布式关系型数据库
  3. HBase:开源Nosql数据库
  4. MongoDB:文档数据库

分布式计算

  1. Hadoop:基于MapReduce分布式计算框架
  2. Spark:在Hadoop基础上使用内存来存储数据
  3. YARN:分布式资源调度

理论

CAP:

  • C:一致性,严格一致性(强一致性:任何时候的状态都一致)
  • A:可用性:指系统一直处于可用状态,每次请求都获得最新数据
  • P:容错性:分布式系统遇到网络故障时仍然能够对外提供服务

CA:放弃容错性,加强一致性和可用性,传统单机数据库

AP:放弃一致性(放弃强一致性,保持最终一致性),如Eureka

CP:放弃可用性,追求一致性和分区,如Zookeeper

在可用性和一致性之间作出选择的解决方案:

故障节点的负载转移给备用节点负责

ACID:

  • 原子性(A):事务要么全部成功,要么全部失败
  • 一致性(C):事务执行之前和执行之后都必须处于一致性状态(如:多个人同时转账无论转账多少次,最后所有人开始的总资金和结束的总资金是一致的)
  • 隔离性(I):当多个用户访问数据库时,数据库每开启一个事务不能被其他事务的操作所干扰
  • 持久性(D):事务一旦提交,数据库的数据就是永久的

分布式事务

二阶段提交

为了使基于分布式系统架构下的所有节点在进行事务提交时保持一致性而设计的一种算法

三个假设

  • 引入协调者和参与者,互相进行网络通信
  • 所有节点都采用预写式日志且日志被写入后即被保持在可靠的存储设备上
  • 所有节点不会永久性损坏,损坏后也可以恢复

image-20230202220701712.png

问题:

  1. 协调者不宕机,参与者不宕机,需要进行回滚
  2. 协调者宕机,参与者不宕机,起新的协调者重复二阶段提交
  3. 全部宕机,无法确认状态,需要数据库管理员接入,防止数据库进入一个不一致的状态

需要注意问题:

  1. 性能问题

    • 两阶段提交需要多次节点间的网络通信,耗时过大,资源需要进行锁定,徒增资源等待时间
  2. 协调单点故障问题

    • 如果事务协调者节点宕机,需要新的协调者,否则参与者处于中间状态无法完成事务
  3. 网络分区带来的数据不一致

    • 一部分参与者收到了commit消息,另一部分参与者没有收到commit消息,导致节点之间数据不一致

三阶段提交

解决:协调单点故障问题

将两阶段提交中的prepare阶段拆成两个阶段:CanCommit和PreCommit机制,解决两个问题:

  1. 单点故障问题
  2. 阻塞问题

并引入超时机制,超时就自动进行事务的提交

image-20230202225937278.png

MVCC

MVCC是并发控制方法,维持一个数据的多个版本使读写操作没有冲突,不加锁, 既不会阻塞写,也不会阻塞读,为每个修改保存一个版本与事务的时间戳相关

实现原理主要依靠记录中的三个隐式字段:undo日志,Read View来实现

作用:提高并发性能,解决脏读的问题。

整体流程:参考博客MVCC多版本并发控制原理总结(最终版) - 郭慕荣 - 博客园 (cnblogs.com)

相关概念:

脏读:读取未提交的数据,如A事务读取B事务未提交的数据,B事务回滚,A事务读取的数据就是脏读

不可重复读:前后多次读取,数据内容不一致,如事务A在执行读取操作,B事务修改了数据,A事务第二次读取发现数据不一致了,读不到重复的数据

解决:使用行级锁(InnoDB) ,锁定该行,读取完成再释放锁

幻读: 前后多次读取,数据总量不一致,如A事务执行读取操作,B事务插入或删除数据,A事务第二次查询发现数据量不同

解决:使用表级锁(MyISAM:效率低) ,锁定整张表,事务A多次读取数据后释放锁,才允许其他事务增加数据

悲观锁:操作数据时直接将数据锁住,直到操作完成后才释放锁,上锁器件其他人不能修改数据

乐观锁:不会上锁,只是在执行更新时判断别人是否修改数据,只有冲突时才放弃操作

数据库三种并发场景:

  1. 读-读:不需要并发控制
  2. 读-写:可能出现脏读、幻读、不可重复读问题
  3. 写-写:存在更新丢失问题

MVCC是用来解决读-写冲突的无锁并发控制,为每个修改保存一个版本。

MVCC+悲观锁:MVCC解决读写冲突,悲观锁解决写写冲突

MVCC+乐观锁:MVCC解决读写冲突,乐观锁解决读写冲突

共识协议

Quorum NWR三要素

  • N:在分布式存储系统中有多少备份数据
  • W:代表一次成功的更新操作要求至少有w份数据成功写入
  • R:代表一次成功的读数据操作要求至少有R份数据成功读取

强一致性要求:W+R > N

RAFT协议

是一种分布式一致性算法,即使出现部分节点故障,网络延时等情况,也不影响各节点,进而提高系统的整体可用性。

Leader:通常一个系统中是一主多从,leader负责处理所有的客户端请求并向follower同步请求日志,当日志同步到大多数节点上后通知follower提交日志

follower:不会发送任何请求,接受并持久化leader同步的日志,当leader出现故障后主动推荐自己为candidate

candidate:leader选举过程中的临时角色

Leader选举过程:

  1. 初始全部follower
  2. current Term+1
  3. 选举自己
  4. 向其他参与者发起投票,票多成为leader,发送心跳

Leader和Follower不同步:leader强制覆盖follower不同步的日志

  1. leader收到写请求w
  2. 将w写入本地log
  3. 向其他follower发起appendEntries RPC
  4. 等待多数派恢复 更新本地状态机,返回客户端,下一个心跳通知follower上一个log已经被提交
  5. follower有问题,leader一直retry

切主: 当leader出现问题需要重新选举

  1. leader发现失去follower响应,失去leader身份
  2. 两个follower之间一段时间没有收到心跳重新进行选举,此时发生切主
  3. leader自杀重启,以follower身份进入

Stale读

在切主过程中,老leader收到读的请求,直接响应就会出现Stale Read

解决方案:保证读的强一致性

课后

1.分布式系统又哪些优势和挑战?

优势:

  • 去中心化
  • 低成本
  • 弹性
  • 资源共享
  • 可靠性高

挑战:

  • 普遍的节点故障
  • 不可靠的网络
  • 异构的机器与硬件环境
  • 安全

2.为什么TCP采用三次握手,而不是两次和四次?

因为两次无法保证客户端和服务端都确认对方接收和发送能力

3.什么是最终一致性?什么是线性一致性?

最终一致性:开始和结束时的状态保持一致,中间无论发生什么变换

线性一致性:保证每个过程都是一致的

4.数据库中的一致性和分布式系统中的一致性有什么区别?

数据库中的一致性是ACID原则中的C,保证事务的一致性,开始和结束的一致性

分布式系统的一致性指强一致性,保证每个节点每次看到的都是相同的数据

5.两阶段提交中,什么场景需要数据库管理员介入?

在协调者和参与者都宕机的情况下,无法判断状态时

6.什么场景适合乐观锁,什么场景适合悲观锁?

读-写操作适合乐观锁,写-写场景适合悲观锁

7.RAFT协议中,Stale读是如何产生的?该如何解决Stale读的问题?

在切主过程中,老的leader还没有变成follower,新的leader选举出来,此时老leader收到读的请求,解决:保证读的强一致性。