让我们正式进入分布式的世界。
目录计划
- 分布式存储漫游指南 1: 2025年了,存储硬件啥样了?
- 分布式存储漫游指南 2: 单机磁盘 IO 的二三事 (同步 I/O 篇)
- 分布式存储漫游指南 3: 单机磁盘 IO 的二三事 (异步 I/O 篇)
- 分布式存储漫游指南 4: 分布式系统的混沌日常
- 分布式存储漫游指南 5: 复制和分区, 我变复杂了、但也可靠了
- 分布式存储漫游指南 6: 控制节点 —— 数据节点的管理、路由与迁移修复
- 分布式存储漫游指南 7: 元数据服务与垃圾回收 (GC)
- 分布式存储漫游指南 8: S3 协议, 对象存储的事实标准
- 分布式存储漫游指南 番外1: CDN, 其实我也是存储节点
- 分布式存储漫游指南 9: 容灾与跨区异步复制
本章更加偏向于分布式存储开发者关心的话题。相对地,其他系统(如分布式计算、分布式流处理)关心的主题可能略有不同,但总体思想是一致的。
Table of Contents
1 分布式系统引言
1.1 为什么需要了解分布式系统理论?
什么是分布式系统?最朴素地理解,就是单机的能力无法性能和容灾要求时,将原本同样的需求扩展到一组节点组成的系统上。一般来讲,分布式系统实现了更大规模的数据处理能力和容灾能力,代价是引入了巨大的复杂性。
为什么要以理论的形式讨论分布式系统?因为我们需要一套理论来回答以下问题
- 系统能否容忍节点失效?如何容忍?代价是什么?
- 数据安全性和用户可用性能否一箭双雕、既要还要?
- 有哪些手段组织数据?优缺点是什么?哪个适合我的系统?
- 是否现有算法实现分布式共识?
- ......
1.2 分布式系统的技术手段是什么?
笔者认为,分布式存储系统实现永远是围绕着两个主题展开的:分区(Parititon) 和复制(Replicate)。
- 撰写分布式系统的技术方案,就是要描述如何解决这两个问题。
- 评审分布式系统的技术方案,就是要帮忙思考和推演这两个问题。
1.3 我是新来的,想总览全局?
笔者推荐先通篇阅读材料1,这本书籍诙谐有趣。
然后阅读数个经典工程论文,以及近年的系统设计论文,对照上书尝试思考开发者问什么要如此设计。比如
实际工程经验总结的博文也非常宝贵。比如4。
1.4 EiB 是什么概念?
开发者可能对系统的延迟量级 (下表) 比较熟悉。表格通过对比,展示了 cpu 时间和网络时间的巨大差异。
操作 | 实际延迟 | 相对 CPU 周期 (1周期=0.3ns) | CPU 视角的比喻 |
---|---|---|---|
1 CPU 周期 | 0.3 ns | 1x | "一瞬间"(CPU 的基本时间单位) |
L1 缓存访问 | 1 ns | 3x | "等 3 步,像走完自家客厅" |
L2 缓存访问 | 4 ns | 13x | "等 13 步,像走到邻居家拿东西" |
主内存访问 (DRAM) | 100 ns | 333x | "等 5 分钟,像下楼取外卖" |
SSD 随机读取 | 16 μs | 53,333x | "等 2 天,像网购快递送达" |
机械硬盘寻道 | 2 ms | 6,666,666x | "等 2 年,像移民去另一个国家" |
局域网往返 (ping) | 0.5 ms | 1,666,666x | "等 8 个月,像跨省搬家" |
互联网往返(跨洲) | 150 ms | 500,000,000x | "等 500 年,像人类文明更迭" |
对于分布式存储系统的数据量,我们也可以用表格表示,便于读者感受量级大小。
存储层级 | 典型容量 | 1Gbps 下载时间 | 时间直观对比 |
---|---|---|---|
CPU L3 Cache | 64 MB | 0.51 秒 | 一次眨眼的时间 |
单机内存 (RAM) | 256 GB | 34 分钟 | 一集电视剧的时间 |
单块 SSD | 4 TB | 8.9 小时 | 一个工作日 |
单块 HDD | 12 TB | 26.7 小时 | 一天多 |
单机磁盘 | 300 TB | 27.8 天 | 一次国际旅行 |
机架磁盘 | 3 PB | 0.76 年 | 一年中所有工作日 |
单数据中心 | 50 PB | 12.7 年 | 工业革命至今的时间 |
大型互联网对象存储 | 5 EB | 1,268 年 | 相当于从宋朝(南宋)到现在的时间跨度 |
全球数据中心总容量 | 10 ZB | 2,536,780 年 | 相当于从最早人类祖先(能人)出现至今的时间 |
足以窥见数据规模之巨。
分布式存储虽不一定非要应对数 PiB/EiB 的数据。比如块存储更加看重性能,而不会追求单盘扩展到数 PiB。
但如果要处理如此规模的持久化存储,只依靠单机显然是无法满足性能和容灾的要求。
1.5 Tips: 不要小看单机硬件的发展速度
本章虽然讨论分布式存储内容,但还请读者不要小看近年来的硬件发展速度。摩尔定律虽然不生效,但 CPU、内存、磁盘每年的发展速度仍然是惊人的。
比如超融合5硬件,虽然对上层软件透明视为单机存储,但底层为全闪阵列存储和虚拟化的计算能力。
被一些云计算唱衰者敬若圭臬的案例,37singal 的下云实践6也指出,人们可能轻视了现代数据中心的硬件的性能。
早在 2004 年,我们在单核赛扬服务器上推出了 Basecamp,只有 256MB 的 RAM, 使用 7,200 RPM 机械硬盘。
......
(而现在)每一台 R7625 都包含两个 AMD EPYC 9454 CPU,运行频率为 2.75GHz,具有 48 核/96 线程。这意味着我们将在本地机群中增加近 4,000 个 vCPU!还有荒谬的 7,680 GB RAM!和 384TB 的第 4 代 NVMe 存储!7
很多 10 年前需要使用分布式系统设计解决的需求,在今天可能单服务器即可完全满足。
可以确定的是,若横向扩展单机系统能满足需求,其永远比扩展到分布式要简便、便宜 ^_^。
2 混沌的分布式环境
2.1 分布式计算的谬误
有一组著名的断言,叫做分布式计算的谬误8,指出了新手容易作出的错误假设。
No. | 断言 | |
---|---|---|
1 | The network is reliable | 网络可靠 |
2 | Latency is zero | 延迟为零 |
3 | Bandwidth is infinite | 带宽是无限的 |
4 | The network is secure | 网络是安全的 |
5 | Topology doesn't change | 拓扑不会改变 |
6 | There is one administrator | 有一个管理员 |
7 | Transport cost is zero | 运输成本为零 |
8 | The network is homogeneous | 网络是同质的 |
这些谬误断言,实际上也指出了分布式系统的重要特性:“失效” 是正常现象。
系统必须将失效作为一等功能去设计和实现。在实际生产工程中,开发者大部分的精力都在和 “失效容忍” 作斗争。
2.2 失效是正常的, 但需要建模和约束
虽然我们意识到失效是正常现象,但必须进行事前的建模和约束。下列失效故障的严重程度明显是不一致的:
- 单节点某一个硬盘读性能大幅度下降
- 单节点(18块硬盘)整机掉电
- 机架(10台节点)网络离线
- 整个机房因火灾掉电
而我们的方案的服务质量承诺 (SLA) 必须具有一个前提,比如
SLA 要求 | 技术手段 |
---|---|
单节点掉线仍然无损 | 使用共识算法保证多数派服务正常 |
容忍整个机房的掉线 | 需要两地三中心模式部署 |
不同失效的容忍要求,直接决定了系统的设计和实现,因此必须提前建好模型,定义故障边界。
系统永远不能保证容忍设计者没有考虑过的错误。
2.3 故障模型 (Failure Model)
故障模型用于描述系统能够容忍的抽象失效。在设计系统前,明确故障模型也非常重要。
文章9 很好地总结了几种常见的故障模型和应对手段。
故障失效模型 | 定义 | 特征 |
---|---|---|
Fail-stop Failures | 系统组件完全停止运行,其故障能立即被系统其他部分察觉,不再发送或接收消息 | - 组件停止且易检测 - 无部分故障或异常行为 - 适用于有故障转移机制的系统 |
Crash Failures | 组件故障并停止运行,但故障可能不会立即被系统其他部分发现 | - 组件无声崩溃,不立即报警 - 需通过超时或通信错误检测 - 系统可能错误地继续与故障组件交互导致延迟 |
Omission Failures | 系统组件未能发送或接收消息,可分为发送遗漏(组件未能发送消息)和接收遗漏(组件未能接收消息) | - 系统继续运行,但消息丢失或未送达 - 可能导致性能下降或数据过时 - 组件本身看似正常,难以检测 |
Temporal Failures | 系统组件在预期时间框架外传递消息,导致性能下降或操作顺序错误 | - 组件发送或接收消息过早或过晚 - 可能导致数据不一致、竞争条件或同步问题 - 对延迟敏感的应用尤其脆弱 |
Byzantine Failures | 组件行为不可预测或恶意,向系统不同部分发送冲突或错误数据 | - 组件可能行为不一致或恶意 - 故障不可预测,可能涉及损坏或伪造的消息 - 需要检测和缓解冲突状态 |
Network Partitions | 组件间通信中断,但每个组件继续独立运行,可能导致“分裂大脑”场景 | - 组件独立运行,导致状态分歧 - 恢复复杂,尤其是在出现显著分歧时 |
Arbitrary Failures | 由硬件问题、内存损坏或软件错误导致的不可预测故障,不遵循标准模式 | - 行为不稳定,难以重现 - 可能导致数据丢失或损坏 |
2.3.1 是否要考虑拜占庭问题?
拜占庭失效是指组件可能向系统不同部分发送冲突或错误数据。比如外部黑客的恶意数据。
一般在公司内网运行的服务,不考虑拜占庭问题。
公共服务,比如 ipfs、以太坊网络,必须要将拜占庭问题作为首要需求考虑。
2.3.2 单向联通的网络分区?
一般模型的 Network Partitions 是指网络的中断,包括发包和收包。单向联通常见于 iptable 的错误配置。在实际工程中,笔者会使用简单的拨测策略检测各节点中的网络情况。
2.4 磁盘故障模式
2.4.1 磁盘故障抽象
在存储领域,磁盘故障主要分为以下几种
磁盘故障类型 | 故障性质 | 处理措施 |
---|---|---|
磁盘读写性能大幅下降 (Slow) | 临时故障或永久故障 | 隔离磁盘,降低磁盘读写权重 |
磁盘无响应 (Hang) | 永久故障 | 超时后隔离该磁盘,立即启动数据迁移 |
磁盘数据损坏 (EIO) | 永久故障 | 立即标记磁盘错误,立即启动数据迁移 |
进一步获取 kernel 日志,分析磁盘失效原因,预测磁盘故障是更佳的。
2.4.2 机械硬盘的故障率
机械硬盘的年故障率(Annualized Failure Rates, AFR)一般参考云存储服务商 Backblaze 发布的报告10。整体大致在 1% - 2% 左右,但不同品牌、型号差异较大。
系统设计应至少满足该故障率的随机磁盘故障。实际还要要考虑整个节点的整体故障,因此系统应该容忍更高的故障率。
2.5 从运维视角思考
谬误断言中有一条叫做 There is one administrator (只有一个管理员)。笔者也是实际参与一些工作后体会到,整个方案的设计如何做到运维友好。
故障的处理、参数调控尽量做到简单有效。
若从运维同事的角度,下面哪个方案更容易接受呢?
对比维度 | 方案A(多参数手动配置) | 方案B(单参数自动调速) |
---|---|---|
参数数量 | 5个(基础速率、时段系数、用户组权重等) | 1个(目标带宽占用率) |
配置方式 | 需在3个窗口手动填写,需校验参数关联性 | 单个窗口填写,系统自动处理细节 |
运维操作复杂度 | 高(需熟记参数依赖关系,多窗口切换) | 低(仅需设置核心目标,无需人工干预) |
故障处理方式 | 需人工分析参数配置是否冲突,手动调整多个参数 | 系统自动检测网络状态,自动回落至安全速率 |
对运维经验依赖度 | 高(依赖管理员个人知识储备) | 低(无需深入理解调速逻辑,傻瓜式操作) |
很显然是方案 B 更有持续性。
笔者曾经在某个系统中设计了类似 A 的方案,以求锦上添花的效果。但是方案不直观导致的中间沟通成本,交付消耗了大量的时间。
2.6 可观测性先行
近年来可观测技术已经成为事实标准,比如 Prometheus + Grafana、OpenTelemtry 等。
在系统初期,也应该在关键路径加入不同分位的观测指标,开发者应当对系统的瓶颈有很清晰的认识。尤其是分析性能问题时,极为有用。
整个分布式系统应当是个白盒模式,而不是黑盒+日志模式。
3 小结
本节从学习分布式系统理论的动机出发,讨论了分布式系统的几个谬误,描述了笔者对于分布式环境的混沌性质的理解。
部分理论的认识确实是经过实践才能体会到,希望能和读者一同交流学习。
在后续,我们将逐步以开发者的角度讨论: CAP 理论、时间、分区和复制等内容。