架构实践03-高可用架构模式
1、CAP 定理
(1)CAP 定理的背景
- 提出者:加州大学伯克利分校的埃里克·布鲁尔(Eric Brewer)在2000年ACM PODC会议上提出。
- 证明者:麻省理工学院的赛斯·吉尔伯特(Seth Gilbert)和南希·林奇(Nancy Lynch)在2002年发表了证明。
(2)CAP 定理的核心内容
- 定义:在一个分布式系统中,不可能同时满足一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Tolerance)三个设计约束,最多只能满足其中的两个。
- 关键要素:
- 一致性(Consistency):所有节点在同一时刻都能看到相同的数据。
- 可用性(Availability):每个请求都能得到成功或者失败的响应。
- 分区容忍性(Partition Tolerance):出现消息丢失或者分区错误时系统能够继续运行。
(3)第一版与第二版解释的差异
- 一致性(Consistency):
- 第一版:所有节点在同一时刻都能看到相同的数据。
- 第二版:对某个指定的客户端来说,读操作保证能够返回最新的写操作结果。
- 可用性(Availability):
- 第一版:每个请求都能得到成功或者失败的响应。
- 第二版:非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)。
- 分区容忍性(Partition Tolerance):
- 第一版:出现消息丢失或者分区错误时系统能够继续运行。
- 第二版:当出现网络分区后,系统能够继续“履行职责”。
(4)CAP 理论的应用
- 分区容忍性(P):在分布式环境中,网络分区是不可避免的,因此必须选择 P。
- CP 架构:保证一致性和分区容忍性,放弃可用性。例如,Zookeeper。
- AP 架构:保证可用性和分区容忍性,放弃一致性。例如,许多NoSQL数据库。
(5)具体系统的选择
- Paxos 算法:提供可靠的最终一致性保证,属于 CP 架构。
- Zookeeper:多数节点正常时可以正常运行,分区中的少数节点会进入 leader 选举状态,不符合 A,基本满足 CP 要求。
(6)实际应用中的注意事项
- 分区容忍性:是最容易保证的,但也是最容易被误解的。分布式系统设计时必须考虑网络分区的可能性。
- 可用性:强调非故障节点在合理时间内返回合理的响应,而不是错误或超时的响应。
- 一致性:强调从客户端的角度来看,读操作能够返回最新的写操作结果。
2、CAP 细节
(1)CAP 关注的数据粒度
- 数据分类:在实际设计过程中,每个系统包含多种类型的数据,不同数据有不同的应用场景和要求。因此,应该将系统内的数据按照不同的应用场景和要求进行分类,每类数据选择不同的策略(CP 或 AP),而不是直接限定整个系统所有数据都是同一策略。
- 示例:用户管理系统中,用户账号数据(用户 ID、密码)选择 CP,用户信息数据(昵称、兴趣、爱好、性别、自我介绍等)选择 AP。
(2)CAP 忽略网络延迟
- 延迟问题:CAP 理论中的 C(一致性)假设数据能够瞬间复制到所有节点,但实际上数据复制总是需要时间。几毫秒或几十毫秒的不一致在某些业务场景下是无法接受的,例如用户余额和商品库存。
- 解决方案:对于严苛的业务场景,可以选择 CA,即单点写入,其他节点做备份,无法做到分布式情况下多点写入。
(3)分区现象
- 分区条件:CAP 理论的前提是系统发生了“分区”现象。如果系统没有发生分区现象(节点间的网络连接一切正常),我们没有必要放弃 C 或 A,应该同时保证 CA。
- 分区恢复:分区期间放弃 C 或 A 并不意味着永远放弃,可以在分区期间进行一些操作,使得分区故障解决后,系统能够重新达到 CA 状态。例如,记录日志并在分区恢复后进行数据恢复。
(4)ACID 和 BASE 的对比
- ACID
- 原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不完成。
- 一致性(Consistency):事务前后数据库的完整性没有被破坏。
- 隔离性(Isolation):防止多个事务并发执行时由于交叉执行而导致数据的不一致。
- 持久性(Durability):事务处理结束后,对数据的修改是永久的。
- BASE
- 基本可用(Basically Available):系统在出现故障时,允许损失部分可用性,保证核心可用。
- 软状态(Soft State):允许系统存在中间状态,不会影响系统整体可用性。
- 最终一致性(Eventual Consistency):系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。
(5)实践中的应用
- 电商网站的高可用系统设计
- 会员模块:包括登录、个人设置、个人订单、购物车、收藏夹等,选择 AP,数据短时间不一致不影响使用。
- 订单模块:下单付款扣减库存操作是核心,选择 CA,在极端情况下可以牺牲 P。
- 商品模块:商户发布或修改商品,用户查看商品,不需要非常强的一致性,可以使用最终一致性,满足可用性和容错性。
- 分布式系统设计
- 数据分类:根据数据的特性和业务需求,选择不同的 CAP 策略。
- 分区恢复:设计分区恢复机制,确保分区故障解决后系统能够恢复到一致状态。
- 日志记录:在分区期间记录日志,以便在分区恢复后进行数据恢复。
3、FEMA:排除架构可用性隐患
(1)FMEA 方法概述
- FMEA(Failure Mode and Effects Analysis,故障模式与影响分析) 是一种广泛应用于各个行业的可用性分析方法。它通过分析系统潜在的故障模式及其影响,帮助识别和解决架构中的可用性隐患。
(2)FMEA 的核心要素
- 功能点
- 从用户角度定义的功能点,而非系统内部模块。
- 例如,用户管理系统中的“登录”和“注册”。
- 故障模式
- 描述系统可能出现的故障现象。
- 无需详细列出故障原因,只需假设故障现象。
- 例如,“MySQL 响应时间达到 3 秒”。
- 故障影响
- 故障模式对功能点的具体影响。
- 尽量精确描述,例如,“20% 的用户无法登录”。
- 严重程度
- 从业务角度评估故障的影响程度。
- 分为“致命 / 高 / 中 / 低 / 无”五个档次。
- 例如,“超过 70% 用户无法登录”属于致命。
- 故障原因
- 列出可能导致故障的具体原因。
- 例如,MySQL 响应慢的原因可能是“没有索引”或“磁盘坏道”。
- 故障概率
- 评估每个故障原因的发生概率。
- 分为“高 / 中 / 低”三档。
- 例如,磁盘坏道的概率较低,而慢查询的概率较高。
- 风险程度
- 综合严重程度和故障概率评估风险等级。
- 风险程度 = 严重程度 × 故障概率。
- 例如,“机房空调烧坏”导致的故障概率较高,风险程度也较高。
- 已有措施
- 当前系统已有的应对措施,包括检测告警、容错、自恢复等。
- 例如,MySQL 主备机切换。
- 规避措施
- 降低故障发生概率的措施,可以是技术手段或管理手段。
- 例如,定期更换服务时间超过 2 年的磁盘。
- 解决措施
- 解决故障的具体措施,主要是技术手段。
- 例如,增加密码重试次数限制。
- 后续规划
- 结合风险程度和现有措施,制定改进计划。
- 例如,增加 MySQL 备机,扩展 Memcache 集群。
(3)FMEA 实战案例
- 假设设计一个简单的用户管理系统,包含登录和注册功能,初始架构为 MySQL 存储、Memcache 缓存、Server 业务处理。通过 FMEA 分析,发现以下改进措施:
- MySQL 增加备机:提高数据可用性。
- Memcache 扩展为集群:提高缓存的可靠性和性能。
- MySQL 双网卡连接:增加网络冗余,提高连接稳定性。
4、高可用存储架构:双机架构
(1)高可用存储架构的本质
- 数据冗余:通过将数据复制到多个存储设备,实现高可用。
- 复杂性:主要体现在如何应对复制延迟和中断导致的数据不一致问题。
(2)常见的高可用存储架构
- 主备复制
- 基本实现:备机主要起备份作用,不承担实际的业务读写操作。
- 优缺点:
- 优点:简单,适用于数据变更频率低的系统。
- 缺点:故障后需要人工干预,无法自动恢复。
- 主从复制
- 基本实现:主机负责读写操作,从机只负责读操作。
- 优缺点:
- 优点:从机提供读操作,提高了硬件利用率,读操作相关的业务可以继续运行。
- 缺点:客户端需要感知主从关系,主从复制延迟可能导致数据不一致,故障后需要人工干预。
- 双机切换
- 设计关键:
- 状态传递:状态传递的渠道和内容。
- 切换决策:切换时机、切换策略、自动程度。
- 常见架构:
* 中介式:引入第三方中介,主备机通过中介传递状态信息。
* 模拟式:备机模拟成客户端,通过读写操作来判断主机状态。
- 主主复制
- 基本实现:两台机器都是主机,互相将数据复制给对方,客户端可以任意挑选其中一台机器进行读写操作。
- 优缺点:
- 优点:无须状态信息传递,也无须状态决策和状态切换。
- 缺点:数据设计有严格要求,适合临时性、可丢失、可覆盖的数据场景。
(3)政府信息公开网站的存储架构选择
- 特点:
- 用户量和QPS不大。
- 读多写少。
- 可忍受一定时间范围的不可用。
- 推荐架构:
- 主备架构:适用于数据变更频率低的系统,故障后可以人工修复。
- 主从架构:读操作多,主机故障后可以从机继续提供读服务。
(4)其他注意事项
- 缓存设计:可以使用缓存(如Redis、CDN)来减轻数据库的压力,提高读操作的性能。
- 中介式切换:可以使用成熟的开源方案(如ZooKeeper、Keepalived)来实现高可用。
- 数据冲突解决:目前主要依靠人工修复,或者覆盖一些数据,或者直接丢失一些数据。
5、高可用存储架构:集群与分区
(1)高可用存储架构的背景
- 单机存储的局限性:随着业务的发展,单台服务器的存储和处理能力无法满足大量数据的需求。
- 数据集群的必要性:为了存储和处理海量数据,必须使用多台服务器组成的集群。
(2)数据集群的类型
- 数据集中集群:1主多备或多从架构,主机负责写操作,备机负责读操作。
- 复杂性:多条复制通道、备机之间的数据一致性、主机状态判断、故障恢复等。
- 典型实现:ZooKeeper 使用 ZAB 算法解决这些问题。
- 数据分散集群:每台服务器存储一部分数据并备份一部分数据,所有服务器都可以处理读写请求。
- 复杂性:数据分配算法、均衡性、容错性、扩展性等。
- 典型实现:Hadoop 的 Namenode 负责数据分区分配,Elasticsearch 的 master node 负责集群管理。
(3)数据分区架构
- 背景:应对地理级别的故障,如自然灾害、大规模停电等。
- 设计要点:
- 数据量:数据量越大,分区规则越复杂。
- 分区规则:洲际分区、国家分区、城市分区,根据业务范围和成本综合考虑。
- 复制规则:
- 集中式备份:存在一个总的备份中心,所有分区的数据都备份到该中心。
- 优点:设计简单,各分区互不影响,扩展容易。
- 缺点:成本较高,需要建设独立的备份中心。
* **<font style="color:rgba(0, 0, 0, 0.9);">互备式备份</font>**<font style="color:rgba(0, 0, 0, 0.9);">:每个分区备份另一个分区的数据。</font>+ **<font style="color:rgba(0, 0, 0, 0.9);">优点</font>**<font style="color:rgba(0, 0, 0, 0.9);">:成本低,利用已有设备。</font>+ **<font style="color:rgba(0, 0, 0, 0.9);">缺点</font>**<font style="color:rgba(0, 0, 0, 0.9);">:设计复杂,扩展麻烦,分区之间互相影响。</font>
* **<font style="color:rgba(0, 0, 0, 0.9);">独立式备份</font>**<font style="color:rgba(0, 0, 0, 0.9);">:每个分区有自己的独立备份中心。</font>+ **<font style="color:rgba(0, 0, 0, 0.9);">优点</font>**<font style="color:rgba(0, 0, 0, 0.9);">:设计简单,扩展容易。</font>+ **<font style="color:rgba(0, 0, 0, 0.9);">缺点</font>**<font style="color:rgba(0, 0, 0, 0.9);">:成本高,每个分区需要独立的备份中心。</font>
(4)远距离集群的挑战
- 网络延迟:远距离通信导致数据同步延迟高,影响用户体验。
- 网络可靠性:远距离网络的可靠性较差,容易出现故障。
- 成本:远距离集群的建设和维护成本高,需要专线支持。
6、计算高可用
(1)服务器的选择
- 集群模式:
- 每个服务器都可以执行任务:类似于计算高性能中的集群,每个服务器都能执行任务,例如常见的网站页面访问。
- 特定服务器执行任务:类似于存储高可用中的集群,只有特定的服务器(通常是“主机”)可以执行任务,例如ZooKeeper的Leader才能处理写操作请求。
(2)任务的重新执行
- 策略一:对于已经分配的任务,即使执行失败也不做任何处理,系统只需要保证新的任务能够分配到其他非故障服务器上执行。
- 策略二:设计一个任务管理器来管理需要执行的计算任务,服务器执行完任务后,需要向任务管理器反馈任务执行结果,任务管理器根据任务执行结果来决定是否需要将任务重新分配到另外的服务器上执行。
(3)常见的计算高可用架构
- 主备架构:
- 冷备:备机上的程序包和配置文件都准备好,但业务系统没有启动,主机故障后需要人工将备机启动并切换任务请求。
- 温备:备机上的业务系统已经启动,但不对外提供服务,主机故障后只需人工切换任务请求。
- 主从架构:
- 正常情况下,主机和备机分别执行不同的计算任务。
- 主机故障后,任务分配器不会自动将任务发送给备机,需要人工操作。
- 集群架构:
- 对称集群:每个服务器的角色都相同,都可以执行所有任务,类似于负载均衡集群。
- 非对称集群:服务器分为不同的角色,不同的角色执行不同的任务,例如Master-Slave架构。
(4)任务分配器
- 任务分配策略:常见的策略包括轮询和随机。
- 状态检测:需要检测服务器的状态(如是否宕机、网络是否正常)和任务的执行状态(如是否卡死、执行时间是否过长)。
(5)非对称集群的特点
- 角色分配:通过算法(如ZAB、Raft)选举Master服务器。
- 任务分配:任务需要划分为不同类型并分配给不同角色的集群节点。
7、业务高可用:异地多活
(1)定义与目标
- 异地:地理位置上不同的地方,类似于“不要把鸡蛋都放在同一篮子里”。
- 多活:不同地理位置上的系统都能提供业务服务,这里的“活”是活动、活跃的意思。
- 目标:在灾难性故障的情况下,业务不受影响或在几分钟内迅速恢复。
(2)应用场景
- 适合业务:对用户影响大的业务,如共享单车、滴滴出行、支付宝、微信等。
- 不适合业务:对用户影响较小的业务,如新闻网站、企业内部IT系统、游戏、博客站点等,可以选择异地备份而非异地多活。
(3)架构模式
- 正常情况:用户无论访问哪个地点的业务系统,都能得到正确的业务服务。
- 异常情况:某个地方业务异常时,用户访问其他地方正常的业务系统,仍能得到正确的业务服务。
(4)成本与复杂度
- 成本上升:需要在多个机房搭建独立的业务系统,增加了软硬件成本和维护成本。
- 复杂度增加:需要设计复杂的异地多活架构,特别是在数据同步和一致性方面。
(5)常见架构类型
- 同城异区
- 定义:业务部署在同一个城市不同区的多个机房。
- 优点:网络传输速度快,复杂度和成本较低。
- 缺点:无法应对大规模自然灾害,如水灾、地震等。
- 适用场景:应对机房级别的故障。
- 跨城异地
- 定义:业务部署在不同城市的多个机房,且距离较远。
- 优点:能够有效应对大规模自然灾害。
- 缺点:网络传输延迟增加,复杂度和成本较高。
- 适用场景:对数据一致性要求不高的业务,如新闻类网站、微博类网站等。
- 跨国异地
- 定义:业务部署在不同国家的多个机房。
- 优点:能够应对全球性的灾害。
- 缺点:数据同步延迟更长,用户体验可能受影响。
- 适用场景:为不同地区用户提供服务,如谷歌搜索、亚马逊等。
(6)数据同步与一致性
- 强一致性数据:如银行存款余额、支付宝余额等,难以实现跨城异地多活,通常采用同城异区架构。
- 弱一致性数据:如用户登录、新闻类网站、微博类网站等,可以采用跨城异地多活。
(7)故障切换与恢复
- 自动切换:通过DNS等负载均衡设备或客户端切换机制实现。
- 数据恢复:在数据不一致的情况下,需要设计机制确保数据最终一致性。
8、异地多活 4 大技巧
(1)保证核心业务的异地多活
- 核心业务优先:在设计异地多活架构时,应优先保证核心业务的高可用性。例如,对于用户子系统,登录是核心业务,而注册和用户信息修改则可以适当放宽要求。
- 业务影响分析:通过分析业务的影响程度来决定哪些业务需要实现异地多活。例如,登录业务直接影响用户体验和业务运行,而注册和用户信息修改的影响相对较小。
(2)保证核心数据最终一致性
- 数据同步挑战:异地多活架构中,数据同步是一个核心问题,但由于物理限制,无法实现所有数据的实时同步。
- 最终一致性:采用最终一致性的策略,即数据最终能够一致,但不要求实时一致。例如,新注册的用户信息可以在5分钟内同步到所有机房。
- 差异化处理:根据数据特征进行差异化处理,例如,账号信息需要强一致性,而用户信息可以容忍一定程度的延迟。
(3)采用多种手段同步数据
- 存储系统同步:利用存储系统自带的同步功能,如MySQL的主备复制、Redis的Cluster功能等。
- 消息队列:使用消息队列进行数据同步,特别是在数据创建且不频繁修改的场景中。
- 二次读取:在异常情况下,如果本地读取失败,可以进行二次读取,从其他中心获取数据。
- 回源读取:在session数据同步中,如果用户在A中心登录后又在B中心登录,B中心可以从A中心获取session数据。
(4)只保证绝大部分用户的异地多活
- 容忍小部分损失:在极端情况下,无法保证100%的业务可用性,需要容忍一小部分用户的损失。
- 用户体验优化:通过公告、补偿等方式减少用户的不满,例如,发送短信通知用户转账结果,减少用户频繁登录系统确认的麻烦。
- 业务降级:在某些情况下,可以采取业务降级措施,例如,将实时转账改为转账申请,后台异步处理。
9、异地多活 4 步走
(1)业务分级
- 目的:降低方案整体复杂度和实现成本。
- 方法:根据业务的重要性和访问量进行分级,挑选出核心业务。
- 常见标准:
- 访问量大的业务(如用户登录)
- 核心业务(如聊天功能)
- 产生大量收入的业务(如广告)
(2)数据分类
- 目的:识别所有核心业务相关的数据及其特征,为后续方案设计提供依据。
- 常见数据特征:
- 数据量:包括总数据量和新增、修改、删除的数据量。
- 唯一性:数据是否要求多个异地机房产生的同类数据必须唯一。
- 实时性:数据同步的时间要求。
- 可丢失性:数据是否可以丢失。
- 可恢复性:数据丢失后是否可以通过某种手段恢复。
(3)数据同步
- 目的:根据数据特点设计不同的同步方案。
- 常见同步方案:
- 存储系统同步:如 MySQL 的主从数据同步、主主数据同步。
- 消息队列同步:如 Kafka、ActiveMQ、RocketMQ 等,适用于无事务性或无时序性要求的数据。
- 重复生成数据:每个机房都可以生成数据,适合于可以重复生成的数据(如登录产生的 session 数据)。
(4)异常处理
- 目的:在出现极端异常情况时,系统能够采取措施应对。
- 常见异常处理措施:
- 多通道同步:采用多种方式同步数据,提高系统的可靠性。
- 同步和访问结合:通过系统的接口进行数据访问,降低跨机房的异地接口访问数量。
- 日志记录:记录关键操作日志,用于故障恢复后的数据修复。
- 用户补偿:通过人工方式对受影响的用户进行补偿,维护用户忠诚度。
(5)业务分级的处理方法
- 根据公司核心目标:优先处理与公司核心目标相关的业务。
- 部门或公司当期的发力点:根据当前的业务重点进行分级。
- 预期货币价值分析:通过风险发生概率、风险损耗成本、技术改造成本等因素进行综合评估。
- 层次分析法:通过两两比较,得到每种业务的重要性权重。
- 数据驱动:根据业务影响的用户百分比、收入影响、投诉人数等数据进行决策。
10、应对接口级故障
(1)接口级故障的表现
- 系统未宕机:业务响应缓慢、大量访问超时、大量访问出现异常(如“无法连接数据库”)。
- 主要原因:系统压力大、负载高,导致无法快速处理业务请求。
(2)导致接口级故障的原因
- 程序 Bug:死循环、数据库慢查询、内存耗尽等。
- 外部因素:黑客攻击、促销或抢购带来的流量激增、第三方系统大量请求、第三方系统响应缓慢等。
(3)应对接口级故障的方法
- 降级
- 定义:系统将某些业务或接口的功能降低,可以是部分功能或完全停掉所有功能。
- 目的:优先保证核心业务和绝大部分用户。
- 实现方式:
- 系统后门降级:通过预留的URL执行降级指令。
- 独立降级系统:实现复杂的权限管理和批量操作。
- 熔断
- 定义:应对依赖的外部系统故障,防止整个系统被拖慢或拖死。
- 实现方式:
- 统一API调用层进行采样或统计。
- 设计合理的阈值,如1分钟内30%的请求响应时间超过1秒则熔断。
- 限流
- 定义:从用户访问压力的角度考虑,只允许系统能够承受的访问量进来,超出部分丢弃。
- 目的:保证一部分请求能够正常响应。
- 实现方式:
- 基于请求限流:限制总量、限制时间量。
- 基于资源限流:限制连接数、文件句柄、线程数、请求队列等。
- 排队
- 定义:限流的变种,让用户等待一段时间而不是直接拒绝。
- 实现方式:
- 使用消息队列(如Kafka)缓存用户请求。
- 调度模块动态调节系统处理能力。
(4)实际案例分析
- 整点限量秒杀系统:
- 登录:流量高峰时降级,减少参与人数,延长登录有效期。
- 抢购:采用排队和限流结合,超出库存的请求直接返回。
- 支付:依赖第三方支付,需要熔断和容错措施,提示用户稍后再支付。