简单聊聊 分布式
1、CAP 原则
CAP原则 指的是在一个 分布式 系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)这三个要素最多只能同时实现两点,不可能三者兼顾。
- 一致性(Consistency):所有的节点上的数据时刻保持同步
- 可用性(Availability):每个请求都能接受到一个相应,无论相应成功或者失败
- 分区容错性(Partition tolerance):系统应该能够持续提供服务,即使系统内部有消息丢失(分区)
补充:
取舍策略:
- CA without P:不要求 分区,保证 一致性 和 可用性
- CP without A:不要求 可用,保证 一致性 和 分区容错性(如:传统的数据库分布式事务)
- AP without C:不要求 一致性,保证 可用性 和 分区容错性(如:NoSQL 分布式事务)
2、高并发
2.1、概述
高并发(High Concurrency),通常是指通过设计保证 系统 能够 同时并行 处理很多请求。
补充:
常见的高并发场景有:
- 淘宝的双11
- 春运时的抢票
- 微博热搜
2.2、高并发的目标
2.2.1、宏观目标
高性能
具体体现为 用户请求的响应时间。
高可用
具体体现为 系统可以正常服务的时间。
高扩展
具体体现为 系统的扩展能力(是否能够快速扩容)。
2.2.2、微观目标
性能指标
平均响应时间(常用,但对慢请求不敏感)
TP90、TP99 等分位值(分值越大,对慢请求越敏感)
将 响应时间 从小到大排序,TP90 表示排在第 90 分位的响应时间。
吞吐量
和响应时间成反比,例如:响应时间 1ms,则吞吐量为 每秒 1000 次。
可用性指标
可用性 = 平均故障时间 / 系统总运行时间。
常用描述(故障可能性,系统基本要求是 保证 3个9 或者 4个9):
可能性 年故障时间 日故障时间 90% 36.5 天 2.4 小时 99% 3.65 天 14.4 分钟 99.9% 8 小时 1.44 分钟 99.99% 53 分钟 8.6 秒 99.999% 5 分钟 0.86秒 可扩展指标
扩展性 = 性能提升比例 / 机器增加比例(通常来说,系统的扩展能力要维持在 70% 以上)。
考虑点:
- 数据库、缓存、消息队列、服务集群 等中间件
- 负载均衡
- 带宽
- 依赖的第三方
2.3、高并发的实践方案
2.3.1、通用的设计方法
纵向扩展(scale-up,单机方面)
- 提升单机的硬件性能:增加内存、CPU 核数、磁盘容量
- 提升单机的软件性能:使用缓存减少 I/O 次数、使用 并发 或 异步 的方式增加吞吐量
横向扩展(scale-out,集群方面)
做好分层架构(横向扩展的前提)
常见的分层架构:
各层进行水平扩展
无状态 做 水平扩容,有状态 做 分片路由;主从同步、读写分离。
2.3.2、实践方案
2.3.2.1、高可用(从 计算 和 I/O 两个维度考虑)
- 集群部署,通过 负载均衡 减轻单机压力
- 多级缓存,包括:静态数据使用 CDN、本地缓存、分布式缓存 等,以及对 缓存场景中的热点 key、缓存穿透、缓存并发、数据一致性 等问题的处理
- 分库分表 和 索引优化,以及借助 搜索引擎 解决复杂查询的问题
- 考虑 NoSQL 数据库的使用,如:HBase、TiDB 等
- 异步化,将次要流程通过 多线程、MQ、延时任务 进行异步处理
- 限流,包括:前端限流、Nginx 接入层限流、服务端限流
- 对流量进行削峰填谷,使用 MQ 承接流量
- 并发处理,通过 多线程 将 串行逻辑 并行化
- 预计算,如:抢红包场景,可以提前计算好红包金额缓存起来,发红包时直接使用即可
- 缓存预热,通过 异步任务 提前预热数据到本地缓存(或分布式缓存)中
- 减少 I/O 次数,如:数据库和缓存的批量读写、RPC 的批量接口支持、使用冗余数据的方式替代 RPC 调用
- 减少 I/O 时的数据包大小,包括:采用轻量级的通信协议、合适的数据结构、去掉接口中的多余字段、减少缓存 key 的大小、压缩缓存 value 等
- 程序逻辑优化,如:将大概率阻断执行流程的判断逻辑前置、for 循环的计算逻辑优化、采用更高效的算法
- 各种池化技术的使用和池大小的设置,包括:HTTP 请求池、线程池、数据库 和 Redis 连接池 等
- JVM 优化,尽可能减少 GC 频率和耗时,包括:新生代和老年代的大小、GC 算法的选择 等
- 锁选择,读多写少的场景使用乐观锁、通过分段锁减少锁冲突
2.3.2.2、高性能(从 冗余、取舍、系统运维 三个方向考虑)
- 对等节点的故障转移:Nginx 和 服务治理框架 均支持一个节点失败后访问另一个节点
- 非对等节点的故障转移:通过 心跳检测 并 实施主备切换,如:Redis 的哨兵模式(或集群模式)、MySQL 的主从切换 等
- 接口层面:超时设置、重试策略、幂等设计
- 降级处理:保证核心服务,牺牲非核心服务,必要时进行熔断;有 备选链路 供核心链路出问题时使用
- 限流处理:对超过系统处理能力的请求直接拒绝(或返回错误码)
- MQ 场景的消息可靠性保证:producer 端的重试机制、broker 端的持久化、consumer 端的 ack 机制 等
- 灰度发布:能支持按机器维度进行小流量部署,观察系统日志和业务指标,等运行平稳后再推全量
- 监控报警:全方位的监控体系,包括:CPU、内存、磁盘、网络的监控,web 服务器、JVM、数据库、各类中间件的监控 和 业务指标的监控
- 灾备演练:类似当前的 “混沌工程”,对系统进行一些破坏性手段,观察局部故障是否会引起可用性问题
2.3.2.3、高可扩展
- 构建合理的分层架构
- 对储存层进行拆分
- 对业务层进行拆分
3、分布式存储(分布式文件系统)架构
3.1、中间控制节点架构
以 HDFS(Hadoop Distribution File System)为代表的架构,在该架构中将 服务器 分为两种类型,一种负责管理 管理数据(元数据),称为 NameNode;另一种负责管理 业务数据(实际数据),称为 DataNode。
当 客户端 需要对某个文件进行读写操作时,首先从 NameNode 获取该文件的位置(具体在哪个 DataNode 中),然后在该位置完成相应的读写操作。
补充:
- NameNode 通常为主备部署
- DataNode 通常是由大量的节点构成一个集群
- 由于 元数据 的访问频度和访问量 相对 数据 都要小得多,因此 NameNode 通常不会称为 性能瓶颈
- DataNode 可以通过 横向扩展 数量 来增加承载力
3.2、完全无中心架构——计算模式
以 Ceph 为代表的架构,在该架构中 客户端 可以直接与存储节点通信(通过一个设备映射关系计算出来其写入数据的位置),从而完全避免中心节点的性能瓶颈。
当 客户端 需要对某个文件进行读写操作时,首先从 Mon 服务 拉取存储资源布局信息,然后根据该布局信息和文件的名称等信息计算出期望文件的位置(包括具体的物理服务器信息和磁盘信息),然后在该位置完成相应的读写操作。
补充:
- 在 Ceph 存储系统架构中核心组件有 Mon 服务、OSD 服务 和 MDS 服务 等
- 对于 块存储类型 只需要 Mon 服务、OSD 服务 和 客户端的软件即可
- Mon 服务 用于维护存储系统的硬件逻辑关系,主要是 服务器 和 磁盘 等在线信息,Mon 服务 通过集群的方式保证其服务的可用性
- OSD 服务 用于实现对 磁盘 的管理,实现真正的数据读写,通常一个磁盘对应一个 OSD 服务
3.3、完全无中心架构——一致性哈希
以 Swift 为代表的架构,该架构与 Ceph 架构的不同之处在于 获取文件位置的方式不同,该架构是通过一致性哈希的方式获得文件位置(即 将设备做成一个哈希环,然后根据文件名称计算出的哈希值映射到哈希环的某个位置,从而实现文件的定位)。
为了保证数据分配的均匀性及出现设备故障时数据迁移的均匀性,一致性哈希将 磁盘 划分为比较多的虚拟分区,每个虚拟分区是哈希环上的一个节点。
整个环是一个从 0 到 32 位最大值的一个区间,并且首尾相接。当计算出文件(或者文件名称)的哈希值后,必然落到哈希环的某个区间,然后以顺时针,必然能找到一个节点(即 文件存储的位置)。