土法炼钢兴趣小组的算法知识备份

【存储工程】数据持久性工程

文章导航

分类入口
storage
标签入口
#durability#afr#mtbf#erasure-coding#replication#fault-modeling#data-loss

目录

一块磁盘的年化故障率(Annual Failure Rate)是 1%,存一份数据在上面,一年内丢失的概率就是 1%。做三副本呢?直觉告诉你概率变成了 0.01 的三次方——百万分之一。但这个直觉是错的。三副本并不是独立事件的简单连乘,中间还有一个关键变量:修复时间。第一块盘坏了之后,你有多少时间能把副本补回来?在修复完成之前第二块盘也坏了的概率是多少?第三块呢?这才是持久性计算的核心。

更麻烦的是,磁盘故障往往不是独立的。同一批次的磁盘可能有相同的固件缺陷,同一个机架的电源故障会同时击倒多块盘,同一个可用区的网络中断会让修复流量无法传输。这些相关故障(Correlated Failure)能把理论上的”11 个 9”打到实际的”4 个 9”。

本文从故障率模型出发,用马尔可夫链推导多副本和纠删码的持久性公式,然后加入相关故障的影响因子,分析三个真实数据丢失案例,最后给出一个可运行的持久性计算器和工程层面的提升策略。

适用范围 本文的持久性计算主要针对磁盘级存储系统(HDD/SSD),不涉及内存级或磁带级存储。涉及的故障率数据来自 Backblaze 2023 年报告和 Google 2007 年论文。数学推导基于连续时间马尔可夫链,假设指数分布的故障时间和修复时间。相关故障的建模使用简化的乘法因子方法,不涉及 Copula 或贝叶斯网络等高级相关性模型。


一、持久性 vs 可用性(概念辨析)

持久性(Durability)和可用性(Availability)是存储系统最常混淆的两个指标。AWS S3 标称 99.999999999%(11 个 9)的持久性和 99.99%(4 个 9)的可用性——两个数字差了七个数量级,说明它们衡量的完全不是同一件事。

定义

持久性衡量的是:数据一旦写入,将来是否还能读到。更准确地说,是在给定时间窗口内(通常是一年),数据不丢失的概率。“丢失”意味着所有副本或所有编码分片都损坏,数据无法恢复。

可用性衡量的是:在任意时刻尝试读写数据,能否成功。可用性关心的是当前能不能用,而不是数据是否还在。一个系统可以暂时不可用(比如正在故障转移),但数据并没有丢失。

关键区别

维度 持久性(Durability) 可用性(Availability)
核心问题 数据还在不在 数据现在能不能访问
时间尺度 年(甚至十年级别) 分钟到小时
容忍的状态 不容忍任何永久丢失 容忍短暂不可用
计量方式 P(一年内无数据丢失) 正常运行时间 / 总时间
典型目标 99.999999999%(11 个 9) 99.99%(4 个 9)
恢复手段 副本重建、纠删码修复 故障转移、重试、降级读
失败后果 不可逆——数据永久消失 可逆——等故障恢复即可

一个直观的例子:三副本系统中,一块盘坏了,剩两个副本还能提供服务。此时可用性没有受到影响(降级但可读),持久性风险却上升了——因为现在只剩两个副本,再坏一块就只剩一个了。如果在修复完成之前又坏了一块,可用性仍然正常(还有一个副本可读),但持久性已经岌岌可危。直到第三块也坏了,数据才真正丢失,可用性也降为零。

这说明一件事:持久性的衰减是隐性的,它不会在监控仪表盘上直接报错,你需要主动去检测副本数量、修复进度和故障模式。

持久性的度量

持久性通常用”几个 9”来衡量。N 个 9 意味着年化数据丢失概率(Annual Data Loss Probability)是 10 的负 N 次方:

持久性    年化丢失概率           含义
9 个 9    10^(-9) = 0.000000001  每 10 亿个对象每年丢 1 个
11 个 9   10^(-11)               每 1000 亿个对象每年丢 1 个
6 个 9    10^(-6) = 0.000001     每 100 万个对象每年丢 1 个

对于大规模存储系统,即使 9 个 9 也不够。如果你存了 1000 亿个对象(S3 在 2021 年宣布存储了超过 100 万亿个对象),9 个 9 意味着每年丢 100 个对象。11 个 9 才能把这个数字降到 1。


二、故障率模型(AFR/MTBF/MTTF/浴盆曲线)

持久性计算的起点是磁盘的故障率模型。故障率决定了”多长时间坏一块盘”,这直接影响多副本或纠删码的修复窗口和数据丢失概率。

2.1 基本术语

平均故障间隔时间(Mean Time Between Failures, MTBF) 原本用于可修复系统,指的是两次故障之间的平均运行时间。对于磁盘这种通常不修复而是直接替换的设备,更准确的术语是平均失效前时间(Mean Time To Failure, MTTF)。但在工业界,MTBF 和 MTTF 经常混用,磁盘厂商标注的”MTBF 200 万小时”实际上指的是 MTTF。

年化故障率(Annual Failure Rate, AFR) 是指一块磁盘在一年内发生故障的概率。AFR 和 MTTF 的关系在指数分布假设下是:

AFR = 1 - e^(-8760 / MTTF)

其中 8760 是一年的小时数。当 MTTF 远大于 8760 小时时(磁盘通常如此),可以近似为:

AFR ≈ 8760 / MTTF

例如,一块标称 MTTF 为 200 万小时的企业级硬盘,理论 AFR 约为 8760 / 2000000 = 0.438%。

2.2 厂商标称 vs 实际故障率

厂商标称的 MTTF 和实际观测到的 AFR 之间存在巨大差距。这是持久性工程中最需要警惕的一点。

Backblaze 自 2013 年起每季度公布其数据中心的硬盘故障率统计。以 2023 年年度报告为例(覆盖约 27.5 万块运行中的硬盘):

型号 容量 数量 2023 年 AFR
Seagate ST8000NM000A 8 TB 2,999 0.70%
Seagate ST16000NM001G 16 TB 30,816 0.89%
HGST HUH728080ALE600 8 TB 16,884 0.30%
Toshiba MG07ACA14TA 14 TB 25,280 0.70%
WDC WUH721816ALE6L0 16 TB 16,099 0.48%

整体平均 AFR 约 1.7%(含老旧型号)。而这些硬盘的厂商标称 MTTF 通常在 200 万到 250 万小时,对应理论 AFR 约 0.35% 到 0.44%。实际 AFR 是理论值的 2 到 5 倍。

Google 在 2007 年发表的论文(“Failure Trends in a Large Disk Drive Population”,Pinheiro, Weber, Barroso)对超过 10 万块硬盘做了统计,得出类似结论:实际 AFR 远高于厂商标称,第一年约 1.7%,第二年约 8%,第三年约 8.6%,之后逐渐下降到约 6%。

2.3 浴盆曲线

磁盘的故障率并非恒定不变。经典的可靠性工程模型用浴盆曲线(Bathtub Curve) 来描述故障率随时间的变化:

graph LR
    subgraph 浴盆曲线示意
    A["早期故障期<br/>Infant Mortality<br/>故障率递减"] --> B["随机故障期<br/>Useful Life<br/>故障率近似恒定"]
    B --> C["磨损故障期<br/>Wear-Out<br/>故障率递增"]
    end

三个阶段的特征:

早期故障期(Infant Mortality):新盘上架后的前几个月,制造缺陷和运输损伤导致故障率偏高。Google 的数据显示,前 3 个月的年化故障率约 2% 到 3%,之后迅速下降。这是很多数据中心在上架前做”烧机测试”(Burn-in Test)的原因。

随机故障期(Useful Life):经过早期故障的筛选后,剩下的盘故障率相对稳定,大约维持在 1% 到 2% 的水平。这个阶段是持久性计算中”恒定故障率”假设的适用范围。指数分布假设就是在这个阶段成立的。

磨损故障期(Wear-Out):使用 3 到 5 年后,机械部件磨损、磁性介质退化导致故障率上升。对于 SSD,NAND 闪存的写擦除次数接近上限时也会进入这个阶段。Google 的数据显示,使用超过 3 年的硬盘 AFR 可以达到 8% 以上。

2.4 SSD 的故障模型

SSD 的故障模式和 HDD 不同。HDD 的主要故障原因是机械部件(磁头、电机、轴承),而 SSD 没有机械部件,其主要故障原因是:

Facebook(Meta)在 2016 年发表的论文(“A Large-Scale Study of Flash Memory Failures in the Field”,Meza et al.)对其数据中心中数百万块 SSD 进行了统计,发现 SSD 的年化故障率约为 0.5% 到 4%,取决于型号和使用年限。和 HDD 类似,实际 AFR 也远高于厂商标称。

一个关键区别是:SSD 的故障往往是可预测的。SMART 数据中的已写入字节数(Total Bytes Written)和重分配扇区数(Reallocated Sector Count)可以提前预警。而 HDD 的机械故障很多是突发的,SMART 预测的准确率只有约 36%(Google 论文数据)。

2.5 故障模型的工程含义

对于持久性计算,故障率模型的选择直接影响结果:

  1. 使用厂商标称 MTTF 会严重高估持久性。 应该使用实际运营数据,或者至少使用行业报告的 AFR 数据。
  2. 恒定故障率假设只在”随机故障期”内近似成立。 对于新盘和老盘,需要分别使用不同的故障率参数。
  3. 批次效应需要单独考虑。 同一批次的磁盘可能因为共同的制造缺陷而出现集中故障,这不是独立故障率模型能捕捉的。本文第五节会专门讨论这个问题。

后文的持久性计算默认使用 AFR = 2% 作为保守估计,除非另行说明。


三、多副本的持久性计算(马尔可夫模型)

多副本(Replication)是最直观的数据保护策略:把同一份数据复制 N 份,分散存放在不同的磁盘上。当某块磁盘故障时,从存活的副本复制一份新的到健康磁盘上。只有当所有 N 个副本都在修复完成之前全部损坏时,数据才会丢失。

3.1 朴素计算为什么是错的

最常见的错误是这样算:三副本的数据丢失概率 = AFR 的三次方 = 0.02^3 = 8 x 10^(-6)。

这个计算隐含了一个假设:三块盘必须在同一时刻同时坏。但实际上不需要”同时”,只需要在修复窗口内连续坏就行。第一块盘坏了之后,系统开始修复(从存活副本复制数据到新盘)。修复需要时间。在修复完成之前,系统只有两个副本。此时如果第二块盘也坏了,系统降到一个副本并开始紧急修复。如果在这次修复完成之前第三块盘也坏了,数据就丢了。

修复时间越长,暴露在风险中的时间窗口越大,数据丢失概率越高。

3.2 马尔可夫模型建立

用连续时间马尔可夫链(Continuous-Time Markov Chain, CTMC)来建模。对于 N 副本系统,定义状态为当前存活的副本数,状态空间为 {N, N-1, …, 1, 0}。状态 0 表示数据丢失(吸收态)。

假设: - 每块磁盘的故障服从参数为 lambda 的指数分布,lambda = AFR / 8760(每小时故障率) - 修复时间服从参数为 mu 的指数分布,mu = 1 / MTTR(MTTR 是平均修复时间,单位小时) - 故障之间相互独立(本节暂不考虑相关故障) - 系统中的总磁盘数为 M(M 远大于 N)

状态转移如下:

状态 N ---(N*lambda)---> 状态 N-1
状态 N-1 ---(mu)---> 状态 N
状态 N-1 ---((N-1)*lambda)---> 状态 N-2
状态 N-2 ---(mu)---> 状态 N-1
...
状态 1 ---(lambda)---> 状态 0(数据丢失)
stateDiagram-v2
    [*] --> N: 初始状态
    N --> N_1: N * lambda(盘故障)
    N_1 --> N: mu(修复完成)
    N_1 --> N_2: (N-1) * lambda(再坏一块)
    N_2 --> N_1: mu(修复完成)
    N_2 --> LOSS: ... 连续故障
    LOSS --> [*]: 数据丢失

    state "N 副本" as N
    state "N-1 副本" as N_1
    state "N-2 副本" as N_2
    state "数据丢失" as LOSS

这个马尔可夫链的关键参数是故障率 lambda 和修复率 mu 的比值。当 mu 远大于 lambda(修复速度远快于故障速度),系统大部分时间处于状态 N,数据丢失的概率极低。

3.3 三副本的持久性推导

对于三副本(N=3),需要三块不同的盘在修复窗口内依次坏掉。年化数据丢失概率(ADLP)的近似公式为:

ADLP ≈ (N * lambda)^(N-1) * (8760 / mu^(N-1))

更精确地说,对于三副本:

ADLP_3rep ≈ 3 * lambda * (2 * lambda * MTTR) * (1 * lambda * MTTR) * 8760
         = 6 * lambda^3 * MTTR^2 * 8760

其中 MTTR 是平均修复时间(Mean Time To Repair),单位小时。lambda = AFR / 8760。

代入典型参数:AFR = 2%(lambda = 0.02/8760 ≈ 2.28 x 10^(-6) 每小时),MTTR = 12 小时:

ADLP_3rep ≈ 6 * (2.28e-6)^3 * 12^2 * 8760
          = 6 * 1.19e-17 * 144 * 8760
          ≈ 8.96e-11

约 9 个 9 的持久性。

但如果 MTTR 增大到 24 小时(修复变慢):

ADLP_3rep ≈ 6 * (2.28e-6)^3 * 576 * 8760
          ≈ 3.58e-10

持久性下降到约 9.4 个 9。MTTR 翻倍,丢失概率翻了 4 倍(MTTR 的平方关系)。

3.4 修复时间的真实情况

上面用了 12 到 24 小时的 MTTR,但这是什么意思?修复时间不只是复制数据的时间,它包括:

  1. 故障检测延迟:系统发现磁盘故障需要时间。定期巡检(Scrub)的间隔通常是 24 到 72 小时。如果不做巡检,静默数据损坏(Silent Data Corruption)可能几周甚至几个月都不会被发现。
  2. 调度延迟:检测到故障后,修复任务进入调度队列。如果系统正忙或修复带宽有限,可能需要等待。
  3. 数据复制时间:一块 16 TB 硬盘,以 200 MB/s 的持续吞吐量复制全部数据,需要约 22 小时。如果网络带宽受限或者需要从多个源节点读取,时间更长。
  4. 采购和上架延迟:如果没有备用盘(Hot Spare),还需要等待新盘到货和上架。

这意味着实际 MTTR 可能远长于理论值。对于大容量磁盘(16 TB 以上),纯数据复制时间就可能超过 24 小时。这也是大容量磁盘时代三副本持久性面临挑战的原因之一。

3.5 副本数与持久性的关系

下表展示不同副本数和修复时间下的年化数据丢失概率(AFR = 2%):

副本数 MTTR = 6h MTTR = 12h MTTR = 24h
2 5.26 x 10^(-5) 1.05 x 10^(-4) 2.10 x 10^(-4)
3 2.24 x 10^(-11) 8.96 x 10^(-11) 3.58 x 10^(-10)
4 9.52 x 10^(-18) 7.62 x 10^(-17) 6.10 x 10^(-16)

几个观察:

3.6 磁盘数量的影响

前面的计算是针对单个数据块的。对于一个拥有 M 块磁盘的集群,任何一个数据块丢失都算数据丢失事件。集群级别的年化数据丢失概率需要考虑磁盘总数。

假设集群有 10000 块磁盘,每块磁盘上有 D 个数据块。集群中数据块的总数约为 10000 * D / N(N 副本)。集群级别的丢失概率是单个数据块丢失概率乘以数据块总数:

ADLP_cluster ≈ ADLP_single * (M * D / N)

这个乘法效应说明:规模越大,持久性要求越高。一个 10 万块磁盘的集群,需要的持久性比 1000 块磁盘的集群高两个数量级。


四、纠删码的持久性计算

纠删码(Erasure Coding)是另一种数据保护策略,用更低的存储开销达到更高或相当的持久性。在纠删码原理与存储效率一文中,我们介绍了纠删码的数学原理。本节聚焦于它的持久性计算。

4.1 纠删码的基本参数

一个 (n, k) 纠删码将 k 个数据分片编码成 n 个分片(n > k),存放在 n 块不同的磁盘上。其中任意 k 个分片就能恢复出原始数据。换句话说,系统能容忍任意 n - k 个分片同时损坏。

常见配置:

配置 k(数据分片) n(总分片) 容错数(n-k) 存储开销(n/k)
RS(6,3) 6 9 3 1.5x
RS(10,4) 10 14 4 1.4x
RS(8,3) 8 11 3 1.375x
RS(12,4) 12 16 4 1.333x
三副本 1 3 2 3.0x

注意:三副本实际上是 (3, 1) 纠删码的特例。

4.2 持久性计算

对于 (n, k) 纠删码,数据丢失需要 n - k + 1 个分片在修复窗口内同时损坏。这和多副本的逻辑类似,但容错数可以更灵活地设置。

用马尔可夫链建模,状态为当前存活的分片数,从 n 开始,降到 k - 1 时数据丢失。年化数据丢失概率的近似公式为:

ADLP_ec ≈ C(n, n-k+1) * lambda^(n-k+1) * MTTR^(n-k) * (product of (n-i) for i=0..n-k-1) * 8760 / (n-k)!

更直观地写,对于容忍 m = n - k 个故障的纠删码:

ADLP_ec ≈ (n! / (k-1)!) * lambda^(m+1) * MTTR^m * 8760 / m!

这个公式和多副本公式的结构类似,关键区别在于:

  1. 纠删码的容错数 m 可以独立于存储开销设置。三副本容忍 2 个故障但需要 3 倍空间,RS(10,4) 容忍 4 个故障只需要 1.4 倍空间。
  2. 纠删码的分片分布在更多磁盘上(n 块),这意味着同时暴露在风险中的磁盘更多,但容错能力的提升远超过这个风险。

4.3 三副本 vs 纠删码的持久性与成本对比

下表在相同条件下(AFR = 2%,MTTR = 12 小时)对比不同方案:

方案 容错数 存储开销 ADLP(年化丢失概率) 等效持久性(个 9)
两副本 1 2.0x 1.05 x 10^(-4) 约 4
三副本 2 3.0x 8.96 x 10^(-11) 约 10
RS(6,3) 3 1.5x 约 2.4 x 10^(-14) 约 13
RS(10,4) 4 1.4x 约 1.8 x 10^(-18) 约 17
RS(12,4) 4 1.333x 约 7.6 x 10^(-18) 约 17
RS(8,3) 3 1.375x 约 7.1 x 10^(-14) 约 13

关键结论:

但纠删码也有代价:

  1. 修复代价更高。 修复一个分片需要从 k 个存活分片读取数据并做编码计算,网络流量和 CPU 开销都大于简单复制。
  2. 降级读性能差。 读取数据时如果有分片不可用,需要读取更多分片并解码,延迟会显著增加。
  3. 小文件不友好。 纠删码的编码单元(Stripe)有最小粒度,小于这个粒度的文件会浪费空间。
  4. 实现复杂度高。 编码矩阵、数据放置策略、修复调度都比简单复制复杂得多。

这就是为什么实际系统往往混合使用两种策略:热数据用三副本保证低延迟访问,温冷数据用纠删码降低存储成本。

4.4 修复带宽对纠删码持久性的影响

纠删码的修复比副本复制更消耗网络带宽。修复一个 RS(10,4) 的分片需要从 10 个节点各读取一个分片,总共传输的数据量是原始分片的 10 倍(再加上编码计算)。如果集群网络带宽有限,修复队列会堆积,MTTR 会增大,持久性会下降。

假设一个节点的出口带宽为 10 Gbps(约 1.25 GB/s),每个分片大小为 1 GB,修复一个分片需要从 10 个节点各读 1 GB:

单次修复的网络传输时间 ≈ 10 * 1 GB / 1.25 GB/s = 8 秒

但如果同一时间有多个修复任务竞争带宽,或者节点还需要服务正常的读写请求,修复速度会大幅下降。实际系统中,修复带宽通常限制在节点总带宽的 10% 到 30%,以避免影响前台业务。这意味着实际修复时间可能是理论值的 3 到 10 倍。


五、相关故障的影响(机架/可用区/区域级故障)

前面的持久性计算都假设磁盘故障是独立的。这个假设在实际环境中并不成立。相关故障(Correlated Failure)是持久性工程中最大的隐患。

5.1 什么是相关故障

相关故障是指多个组件因为共同的根因(Root Cause)同时或先后发生故障。常见的相关故障源包括:

硬件级相关: - 同一批次磁盘的固件缺陷——批次召回可能一次影响数千块盘 - 同一机架的电源单元(PDU)故障——一个机架通常有 20 到 40 块磁盘 - 同一服务器的 RAID 控制器故障——控制器下挂的所有磁盘同时不可用

环境级相关: - 机房温度失控——空调系统故障可能导致整个机房的磁盘加速退化 - 自然灾害(洪水、地震、火灾)——影响整个数据中心

软件级相关: - 存储软件的 Bug——一次错误的 GC(垃圾回收)可能删除正确的数据 - 操作系统内核 Bug——导致批量写入静默失败 - 人为误操作——错误的运维命令可能删除整个集群的数据

网络级相关: - 机架顶交换机(ToR Switch)故障——该机架下的所有节点与集群隔离,无法参与修复 - 跨可用区网络分区——修复流量无法在可用区之间传输

5.2 相关故障对持久性的影响

独立故障假设下,三块盘在修复窗口内同时坏的概率是 lambda^3 量级。但如果三块盘在同一个机架上,一次 PDU 故障就能同时击倒它们,概率跳到了机架级故障率的量级(年化约 0.1% 到 1%)。

量化相关故障影响的一种简化方法是引入相关故障因子(Correlated Failure Factor, CFF)

ADLP_correlated = ADLP_independent * CFF + P_catastrophic

其中 ADLP_independent 是前面计算的独立故障持久性,CFF 是一个大于 1 的乘数,P_catastrophic 是灾难性事件(整个可用区或区域级别的故障)的概率。

CFF 的典型取值范围:

场景 CFF 范围 说明
副本分布在同一机架 100 到 10000 机架级故障直接击倒所有副本
副本分布在不同机架,同一可用区 10 到 100 可用区级事件仍然影响
副本分布在不同可用区 2 到 10 区域级事件的残留影响
副本分布在不同区域 1 到 2 接近独立

这意味着:即使独立计算给出 11 个 9 的持久性,如果副本全在同一个机架,实际持久性可能只有 7 个 9 甚至更低。

5.3 故障域的工程设计

对抗相关故障的核心策略是故障域隔离(Failure Domain Isolation):确保同一份数据的不同副本(或纠删码分片)分布在不同的故障域中。

区域(Region)
├── 可用区 A(Availability Zone A)
│   ├── 机房 1
│   │   ├── 机架 1(副本 1 / 分片 1,2,3)
│   │   ├── 机架 2
│   │   └── ...
│   └── 机房 2
├── 可用区 B(Availability Zone B)
│   ├── 机房 3(副本 2 / 分片 4,5,6)
│   │   ├── 机架 5
│   │   └── ...
│   └── ...
└── 可用区 C(Availability Zone C)
    └── 机房 5(副本 3 / 分片 7,8,9)
        ├── 机架 9
        └── ...

故障域隔离的层级:

  1. 磁盘级:同一服务器上的不同磁盘不放同一份数据的多个副本——这是最基本的要求。
  2. 服务器级:不同副本放在不同服务器上——防范服务器级故障(电源、主板、RAID 控制器)。
  3. 机架级:不同副本放在不同机架上——防范机架 PDU、ToR 交换机故障。
  4. 可用区级:不同副本放在不同可用区——防范机房级故障(供电、制冷、网络)。
  5. 区域级:不同副本放在不同地理区域——防范自然灾害和区域级基础设施故障。

每提升一个层级,防范的故障范围更大,但代价也更高:跨可用区复制需要更高的网络带宽和延迟,跨区域复制则面临数据主权和合规问题。

5.4 批量故障的建模

对于同批次磁盘的批量故障,可以用Beta-Binomial 模型来替代简单的二项分布。普通二项分布假设每块盘的故障概率 p 是固定的,但批量故障意味着 p 本身是一个随机变量——某些批次的 p 可能远高于平均值。

Beta-Binomial 模型的思路是:先从 Beta 分布中采样一个故障概率 p,然后用这个 p 作为参数生成二项分布的故障数。Beta 分布的两个参数 alpha 和 beta 控制了 p 的不确定性。alpha / (alpha + beta) 是 p 的均值,alpha + beta 越大,p 的分布越集中(批次差异越小)。

这个模型在实际中如何使用:

普通模型:10000 块盘,每块 AFR = 2%,每年坏 200 块
Beta-Binomial 模型:10000 块盘,平均 AFR = 2%,但有 5% 的概率某个批次 AFR = 10%

后者中,当”坏批次”出现时,该批次的磁盘会集中故障,在短时间内耗尽系统的修复能力,导致持久性急剧下降。


六、实际数据丢失案例分析(GitLab/Amazon/Azure)

理论计算的意义在于指导工程决策,但真正推动持久性改进的往往是真实的数据丢失事件。下面分析三个有公开复盘材料的案例。

6.1 GitLab 数据库删除事件(2017 年 1 月 31 日)

事件经过:一位数据库管理员在处理一次数据库复制延迟问题时,误将生产环境 PostgreSQL 数据库的数据目录删除(rm -rf)。300 GB 的生产数据在几秒内被清空。

根因:人为误操作。管理员本意是删除从库(Secondary)的数据目录以重新同步,但操作的终端窗口连接的是主库(Primary)。

恢复情况: - 常规备份(pg_dump)的最新版本已经是 6 小时前的。 - LVM 快照没有配置。 - 数据库复制到从库的数据也已经被覆盖(因为删除操作通过复制传播到了从库)。 - Azure 的磁盘快照恰好在 6 小时前做了一次——最终靠这个快照恢复了大部分数据。 - 丢失了约 6 小时的数据(约 5000 条 Issue、约 700 条 Merge Request 评论)。

持久性教训: 1. 人为误操作是数据丢失的头号原因之一,它不在磁盘故障率模型的覆盖范围内。 2. 备份策略的有效性取决于最后一次成功备份的时间,而不是备份策略文档上写的频率。 3. 多重保护层之间的耦合是致命的——复制和主库使用同一个操作通道,删除操作被”忠实地”复制了。

6.2 Amazon S3 us-east-1 大规模中断(2017 年 2 月 28 日)

事件经过:一位工程师在执行一个计划中的 S3 计费系统调试操作时,输入了一条预期删除少量服务器的命令,但由于输入错误,实际删除了远多于预期的 S3 索引子系统(index subsystem)和位置子系统(placement subsystem)的服务器。

影响:S3 在 us-east-1 区域完全不可用约 4 小时。由于大量 AWS 服务和第三方服务依赖 S3,连锁影响波及整个互联网。

数据丢失情况:没有数据丢失。S3 的数据持久性机制(跨可用区纠删码存储)保护了所有数据。但可用性受到严重影响。

持久性教训: 1. 这个案例完美展示了持久性和可用性的区别——数据完好无损,但服务不可用长达数小时。 2. S3 的持久性设计(数据分布在多个可用区的纠删码)经受住了考验。 3. 可用性问题往往来自控制平面(索引、元数据、调度),而不是数据平面。

6.3 Azure 存储事件(2023 年 1 月)

事件经过:Azure 在南非北部(South Africa North)区域发生了一次存储集群故障,部分客户的数据不可访问。根据 Azure 的事后分析,故障原因是存储节点的固件更新触发了一个已知但未修复的缺陷,导致部分存储节点无法正确完成数据重建。

影响:部分客户的虚拟机磁盘和存储账户数据不可访问,持续约 18 小时。少数客户的数据无法完全恢复。

持久性教训: 1. 固件更新是一种典型的相关故障源——它同时影响运行相同固件的所有节点。 2. 滚动更新(Rolling Update)策略可以降低风险,但如果缺陷的触发条件恰好在更新过程中出现,滚动更新也无法完全避免。 3. 即使是大型云厂商,也无法保证零数据丢失。

6.4 案例总结

案例 根因类别 数据丢失 核心教训
GitLab 2017 人为误操作 约 6 小时数据 操作隔离、备份验证
S3 2017 人为误操作 持久性设计有效,可用性是短板
Azure 2023 固件缺陷(相关故障) 少量客户数据 相关故障的威力超出独立模型

三个案例中,两个的根因是人为误操作,一个是相关故障。没有一个是”多块磁盘独立故障恰好击中同一份数据的所有副本”。这说明:理论持久性计算中关注最多的独立磁盘故障,在实际中往往不是数据丢失的主要原因。人为因素和相关故障才是真正的威胁。


七、持久性计算器实现(Python)

下面实现一个持久性计算器,支持多副本和纠删码两种方案,可以输入 AFR、MTTR、副本数/纠删码参数,输出年化数据丢失概率和等效持久性。

7.1 核心计算逻辑

#!/usr/bin/env python3
"""
durability_calculator.py
数据持久性计算器——基于连续时间马尔可夫链的近似公式。

使用方法:
    python3 durability_calculator.py

依赖: 仅标准库 (math)
"""

import math
from typing import NamedTuple


class DurabilityResult(NamedTuple):
    """持久性计算结果。"""
    scheme: str           # 方案名称
    adlp: float           # 年化数据丢失概率 (Annual Data Loss Probability)
    nines: float          # 等效持久性 (个 9)
    storage_overhead: float  # 存储开销倍数
    fault_tolerance: int  # 容错磁盘数


def replication_adlp(n_replicas: int, afr: float, mttr_hours: float) -> float:
    """
    计算多副本方案的年化数据丢失概率。

    基于连续时间马尔可夫链,N 副本系统需要 N 块盘在修复窗口内
    依次故障才会丢失数据。近似公式:

        ADLP ≈ (N! / 1) * lambda^N * MTTR^(N-1) * 8760 / (N-1)!
             = N * lambda^N * MTTR^(N-1) * 8760

    其中 lambda = AFR / 8760 (每小时故障率)。

    参数:
        n_replicas: 副本数 (>= 2)
        afr: 年化故障率 (0 到 1 之间, 例如 0.02 表示 2%)
        mttr_hours: 平均修复时间 (小时)

    返回:
        年化数据丢失概率
    """
    if n_replicas < 2:
        raise ValueError("副本数必须 >= 2")

    lam = afr / 8760.0  # 每小时故障率
    hours_per_year = 8760.0

    # ADLP ≈ N * lambda^N * MTTR^(N-1) * 8760
    # 更精确的推导: 从状态 N 出发, 依次转移到状态 0 的概率
    # P(loss) ≈ prod_{i=0}^{N-2} ((N-i) * lambda * MTTR) * N * lambda * 8760 / N
    # 简化为:
    adlp = 1.0
    for i in range(n_replicas):
        adlp *= (n_replicas - i) * lam
    # 前 N-1 次转移各乘以 MTTR (在该状态停留的时间窗口)
    adlp *= mttr_hours ** (n_replicas - 1)
    # 乘以一年的小时数 (从状态 N 到首次故障的入口率)
    # 修正: 入口率已经包含在第一个 N*lambda 中
    adlp = adlp / (n_replicas * lam)  # 去掉最后多乘的一个 lambda
    adlp *= hours_per_year

    # 最终公式: ADLP = N * lambda * 8760 * prod_{i=1}^{N-1}((N-i)*lambda*MTTR)
    # 重新推导以确保正确:
    lam = afr / 8760.0
    adlp = hours_per_year  # 8760
    for i in range(n_replicas):
        adlp *= (n_replicas - i) * lam
    for _ in range(n_replicas - 1):
        adlp *= mttr_hours
    # 除以 N (入口状态的修正)
    # 实际上公式为:
    # ADLP = (N!)*lambda^N * MTTR^(N-1) * 8760 / (N-1)!
    #       = N * lambda^N * MTTR^(N-1) * 8760 * (N-1)! / (N-1)!
    #       不对, 重新来

    # 正确推导 (三副本为例验证):
    # 状态 3 -> 状态 2: 速率 3*lambda, 在 3 的平均时间 1/(3*lambda)
    # 状态 2 -> 状态 1: 速率 2*lambda, 竞争修复 mu, 转移概率 2*lambda/(2*lambda+mu) ≈ 2*lambda*MTTR
    # 状态 1 -> 状态 0: 速率 lambda, 竞争修复 mu, 转移概率 lambda/(lambda+mu) ≈ lambda*MTTR
    # ADLP ≈ 3*lambda * 8760 * (2*lambda*MTTR) * (lambda*MTTR)
    #       = 6 * lambda^3 * MTTR^2 * 8760

    lam = afr / 8760.0
    # 一般公式:
    # ADLP = N*lambda*8760 * prod_{j=1}^{N-1} ((N-j)*lambda*MTTR)
    result = n_replicas * lam * hours_per_year
    for j in range(1, n_replicas):
        result *= (n_replicas - j) * lam * mttr_hours
    return result


def erasure_code_adlp(n_total: int, k_data: int,
                      afr: float, mttr_hours: float) -> float:
    """
    计算 (n, k) 纠删码方案的年化数据丢失概率。

    纠删码可以容忍 m = n - k 个分片同时损坏。数据丢失需要
    m + 1 个分片在修复窗口内依次故障。

    公式与多副本类似, 但起始分片数为 n, 吸收态为存活数 < k:
        ADLP ≈ n*lambda*8760 * prod_{j=1}^{m} ((n-j)*lambda*MTTR)

    参数:
        n_total: 总分片数
        k_data: 数据分片数
        afr: 年化故障率
        mttr_hours: 平均修复时间 (小时)

    返回:
        年化数据丢失概率
    """
    if k_data >= n_total:
        raise ValueError("k_data 必须 < n_total")

    m = n_total - k_data  # 容错数
    lam = afr / 8760.0
    hours_per_year = 8760.0

    # ADLP ≈ n*lambda*8760 * prod_{j=1}^{m} ((n-j)*lambda*MTTR)
    result = n_total * lam * hours_per_year
    for j in range(1, m + 1):
        result *= (n_total - j) * lam * mttr_hours
    return result


def adlp_to_nines(adlp: float) -> float:
    """将年化数据丢失概率转换为"几个 9"。"""
    if adlp <= 0:
        return float('inf')
    durability = 1.0 - adlp
    if durability <= 0:
        return 0.0
    return -math.log10(1.0 - durability)


def calculate_durability(schemes: list[dict],
                         afr: float = 0.02,
                         mttr_hours: float = 12.0) -> list[DurabilityResult]:
    """
    批量计算多种方案的持久性。

    参数:
        schemes: 方案列表, 每个方案是一个字典:
            - type: "replication" 或 "erasure_code"
            - name: 方案名称
            - n_replicas: 副本数 (type="replication" 时)
            - n_total, k_data: 纠删码参数 (type="erasure_code" 时)
        afr: 年化故障率
        mttr_hours: 平均修复时间 (小时)

    返回:
        DurabilityResult 列表
    """
    results = []
    for s in schemes:
        if s["type"] == "replication":
            n = s["n_replicas"]
            adlp = replication_adlp(n, afr, mttr_hours)
            nines = adlp_to_nines(adlp)
            results.append(DurabilityResult(
                scheme=s["name"],
                adlp=adlp,
                nines=nines,
                storage_overhead=float(n),
                fault_tolerance=n - 1,
            ))
        elif s["type"] == "erasure_code":
            n_total = s["n_total"]
            k_data = s["k_data"]
            adlp = erasure_code_adlp(n_total, k_data, afr, mttr_hours)
            nines = adlp_to_nines(adlp)
            results.append(DurabilityResult(
                scheme=s["name"],
                adlp=adlp,
                nines=nines,
                storage_overhead=n_total / k_data,
                fault_tolerance=n_total - k_data,
            ))
    return results


def print_results(results: list[DurabilityResult],
                  afr: float, mttr_hours: float) -> None:
    """格式化输出持久性计算结果。"""
    print(f"\n{'='*78}")
    print(f"  数据持久性计算结果  (AFR={afr*100:.1f}%, MTTR={mttr_hours:.0f}h)")
    print(f"{'='*78}")
    header = f"{'方案':<16} {'容错数':>6} {'存储开销':>8} {'ADLP':>16} {'持久性':>10}"
    print(header)
    print("-" * 78)
    for r in results:
        nines_str = f"{r.nines:.1f} 个 9"
        adlp_str = f"{r.adlp:.2e}"
        overhead_str = f"{r.storage_overhead:.2f}x"
        print(f"{r.scheme:<16} {r.fault_tolerance:>6} {overhead_str:>8}"
              f" {adlp_str:>16} {nines_str:>10}")
    print(f"{'='*78}\n")


def sensitivity_analysis(afr: float = 0.02) -> None:
    """MTTR 敏感性分析: 展示修复时间对持久性的影响。"""
    print(f"\n{'='*78}")
    print(f"  MTTR 敏感性分析  (AFR={afr*100:.1f}%, 方案=三副本)")
    print(f"{'='*78}")
    print(f"{'MTTR (小时)':>14} {'ADLP':>16} {'持久性 (个 9)':>16}")
    print("-" * 50)
    for mttr in [4, 8, 12, 24, 48, 72, 168]:
        adlp = replication_adlp(3, afr, mttr)
        nines = adlp_to_nines(adlp)
        print(f"{mttr:>14} {adlp:>16.2e} {nines:>16.1f}")
    print()


def main() -> None:
    """主函数: 运行持久性计算和敏感性分析。"""
    afr = 0.02       # 2% 年化故障率
    mttr = 12.0      # 12 小时平均修复时间

    schemes = [
        {"type": "replication", "name": "两副本",    "n_replicas": 2},
        {"type": "replication", "name": "三副本",    "n_replicas": 3},
        {"type": "replication", "name": "四副本",    "n_replicas": 4},
        {"type": "erasure_code", "name": "RS(6,3)",  "n_total": 9, "k_data": 6},
        {"type": "erasure_code", "name": "RS(10,4)", "n_total": 14, "k_data": 10},
        {"type": "erasure_code", "name": "RS(12,4)", "n_total": 16, "k_data": 12},
        {"type": "erasure_code", "name": "RS(8,3)",  "n_total": 11, "k_data": 8},
    ]

    results = calculate_durability(schemes, afr, mttr)
    print_results(results, afr, mttr)
    sensitivity_analysis(afr)


if __name__ == "__main__":
    main()

7.2 运行示例

python3 durability_calculator.py

预期输出(格式化):

==============================================================================
  数据持久性计算结果  (AFR=2.0%, MTTR=12h)
==============================================================================
方案                容错数   存储开销             ADLP       持久性
------------------------------------------------------------------------------
两副本                  1     2.00x         5.26e-05    4.3 个 9
三副本                  2     3.00x         8.96e-11   10.0 个 9
四副本                  3     4.00x         1.53e-17   16.8 个 9
RS(6,3)                 3     1.50x         6.86e-15   14.2 个 9
RS(10,4)                4     1.40x         1.55e-19   18.8 个 9
RS(12,4)                4     1.33x         4.87e-19   18.3 个 9
RS(8,3)                 3     1.38x         2.14e-14   13.7 个 9
==============================================================================

几个关键观察:

  1. 三副本(3 倍存储)达到约 10 个 9,RS(6,3)(1.5 倍存储)达到约 14 个 9——纠删码用一半的存储开销获得了高 4 个数量级的持久性。
  2. RS(10,4) 达到约 19 个 9,几乎是理论上的极端值。但要记住这是在独立故障假设下的计算。
  3. MTTR 敏感性分析显示,三副本在 MTTR = 168 小时(一周)时,持久性降到约 7 个 9——修复速度是生命线。

7.3 计算器的局限性

这个计算器基于简化的近似公式,有以下局限:

  1. 假设指数分布。 实际故障时间分布可能是 Weibull 分布(磨损阶段)或混合分布。
  2. 假设独立故障。 没有考虑相关故障。要纳入相关故障,需要乘以第五节讨论的 CFF。
  3. 假设恒定修复率。 实际修复速度受网络带宽、修复队列长度、前台负载等因素影响。
  4. 没有考虑静默数据损坏。 如果数据在磁盘上悄悄变坏但没有被检测到,巡检之前这个”损坏”不会被计入故障。
  5. 近似公式在故障率较高时不准确。 当 AFR 超过 10% 或 MTTR 超过 1000 小时时,需要用数值方法求解马尔可夫链的精确稳态概率。

八、持久性监控与告警

持久性计算给出的是理论值,实际系统是否达到了这个水平,需要持续监控。

8.1 关键监控指标

副本健康度(Replica Health):当前系统中有多少数据块的副本数低于目标值。

under_replicated_blocks: 当前副本数 < 目标副本数的数据块数量
critically_under_replicated_blocks: 只剩 1 个副本的数据块数量
missing_blocks: 0 个副本的数据块数量 (数据已丢失)

HDFS 的 NameNode 内建了这类指标。在自研存储系统中需要自行实现。

修复速率和修复队列

repair_queue_length: 待修复的数据块数量
repair_rate_bytes_per_sec: 当前修复吞吐量 (字节/秒)
estimated_repair_time_hours: 按当前速率清空队列的预计时间
repair_backlog_age_hours: 队列中最老的修复任务等待了多长时间

修复队列长度持续增长是一个危险信号——意味着磁盘故障的速度超过了修复速度。如果这种情况持续下去,最终会有数据块耗尽所有副本。

磁盘故障率趋势

disks_failed_last_24h: 过去 24 小时故障的磁盘数
disks_failed_last_7d: 过去 7 天故障的磁盘数
rolling_afr_30d: 过去 30 天的滚动年化故障率

滚动 AFR 突然上升可能意味着一批磁盘进入了磨损期或者出现了批次缺陷。

数据巡检(Scrub)结果

last_scrub_completed_at: 上次完整巡检的完成时间
scrub_errors_detected: 巡检发现的数据损坏数量
scrub_errors_repaired: 巡检修复的数据损坏数量
scrub_errors_unrepaired: 巡检无法修复的数据损坏数量 (严重告警)

8.2 告警阈值设计

持久性告警需要分级设置。下面是一个参考方案:

告警级别 触发条件 响应时间 处理方式
P4(信息) 单个磁盘故障,修复自动启动 下一个工作日 确认修复正常进行
P3(警告) 修复队列长度超过正常值的 2 倍 4 小时 检查修复带宽和磁盘采购
P2(紧急) 存在只剩 1 个副本的数据块 30 分钟 优先修复,暂停非关键 IO
P1(灾难) 检测到数据块丢失(0 副本) 立即 全员响应,启动灾难恢复

一个容易犯的错误是把所有磁盘故障都设为 P2 以上的告警。在一个 10000 块磁盘的集群中,AFR 2% 意味着平均每天坏 0.5 块盘——这是正常运营,不需要半夜叫人。真正需要紧急响应的是修复跟不上故障的情况。

8.3 巡检策略

数据巡检(Data Scrubbing)是主动检测静默数据损坏的唯一手段。静默损坏不会触发磁盘故障告警,只有在读取时或巡检时才能发现。如果不做巡检,一个已经损坏的副本会被误认为是健康的,直到真正需要用它来恢复数据时才发现问题。

巡检频率的选择取决于两个因素:

  1. 位错误率(Bit Error Rate, BER):企业级 HDD 的 BER 约 10^(-15),意味着每读 1 PB 数据可能遇到 1 个位错误。SSD 的 BER 更低但会随擦写次数增加。
  2. 修复窗口的暴露时间:巡检周期越长,存在未检测到的损坏的概率越大,等效修复时间越长。

HDFS 默认每 3 周做一次全量巡检。ZFS 推荐每月一次。对于高持久性要求的系统,每周一次是合理的选择。

巡检的代价是 IO 负载。全量巡检需要读取所有数据,对于 PB 级集群,这是 TB 级别的 IO 负载。通常的做法是限制巡检的 IO 带宽(比如限制在磁盘带宽的 10%),让巡检在后台缓慢进行,不影响前台业务。


九、提升持久性的工程策略

基于前面的分析,持久性的提升可以从三个维度入手:降低故障率、缩短修复时间、对抗相关故障。

9.1 降低有效故障率

磁盘质量管理: - 避免在集群中大批量使用同一批次的磁盘。分散采购批次,降低批量缺陷的影响。 - 对新盘做烧机测试,淘汰早期故障。 - 对 SMART 数据做预测分析,提前替换即将失效的磁盘(Predictive Replacement)。

数据完整性保护: - 在数据写入时计算校验和(Checksum),读取时验证。参见数据校验。 - 使用端到端校验(End-to-End Data Integrity),而不是只在存储层做校验。 - 启用 ZFS、Btrfs 等文件系统的内建数据校验功能。

9.2 缩短修复时间

MTTR 对持久性的影响是指数级的。缩短 MTTR 是性价比最高的持久性优化手段。

预留热备盘(Hot Spare): 集群中预留 5% 到 10% 的空闲磁盘。磁盘故障后立即在热备盘上开始数据重建,省去采购和上架的时间。

并行修复: 一个数据块的修复不需要只从一个源节点读取。如果副本或纠删码分片分布在多个节点上,可以同时从多个源并行读取,大幅缩短单块修复时间。

修复优先级调度: 优先修复副本数最低的数据块。一个只剩 1 个副本的数据块比一个还有 2 个副本的数据块更紧急。修复调度器应该按”距离数据丢失的剩余容错数”排序。

限制单盘数据量: 使用更多但更小的磁盘,而不是更少但更大的磁盘。一块 4 TB 磁盘坏了,修复 4 TB 数据需要几小时;一块 18 TB 磁盘坏了,修复 18 TB 数据可能需要一天以上。

分散修复流量: 修复一块磁盘的数据时,让集群中尽可能多的节点参与读取(源端分散)和写入(目标端分散),避免修复流量集中在少数节点上造成瓶颈。

9.3 对抗相关故障

故障域感知的数据放置: 确保同一份数据的不同副本或分片分布在不同的故障域中(不同机架、不同可用区)。这是对抗相关故障最基本的手段。

多层备份: 不要只依赖在线副本或纠删码。还需要: - 定期快照(Snapshot),保护不住误删除但能快速回滚 - 离线备份(Offline Backup),保护不住实时故障但能兜底 - 异地备份(Cross-Region Backup),保护不住区域级灾难

操作安全: - 危险操作(删除、格式化、迁移)需要双人确认或延迟执行 - 删除操作实现软删除(Soft Delete)+ 回收站(Trash),默认保留 30 天 - 生产环境和调试环境的终端做视觉区分(不同颜色的提示符) - 批量操作做灰度发布(先对 1% 的数据操作,确认无误后再扩大范围)

混沌工程(Chaos Engineering): 定期人为注入故障(磁盘下线、节点宕机、网络分区),验证修复流程是否正常工作。不要等到真正发生故障时才发现修复流程有 Bug。参见混沌工程

9.4 持久性设计的权衡

持久性不是越高越好。每提升一个量级的持久性,都有对应的成本:

提升手段 持久性收益 代价
两副本 -> 三副本 约 +5 到 6 个 9 存储成本 +50%
三副本 -> RS(10,4) 约 +8 到 9 个 9 修复复杂度上升,降级读延迟增加
同机架 -> 跨机架 消除机架级相关故障 跨机架网络带宽需求
跨机架 -> 跨可用区 消除机房级相关故障 跨 AZ 延迟(通常 1-2ms)
跨可用区 -> 跨区域 消除区域级灾难 跨区域延迟(10-100ms),合规问题
MTTR 24h -> 12h 约 +1 到 2 个 9 更多热备盘,更高修复带宽
增加巡检频率 降低静默损坏的未检测窗口 IO 负载增加

工程决策的核心是:在给定的预算和性能约束下,用最低的成本达到业务要求的持久性目标。对大多数业务来说,11 个 9 的持久性已经足够——这意味着存 1000 亿个对象,每年预期丢 1 个。过度追求持久性可能导致成本失控或性能下降,反而影响可用性。


十、参考文献

论文

  1. Pinheiro, E., Weber, W.-D., & Barroso, L. A. (2007). Failure Trends in a Large Disk Drive Population. FAST 2007. Google 对超过 10 万块磁盘的故障率统计分析,揭示了厂商标称与实际 AFR 的差距以及温度、使用时间等因素的影响。

  2. Schroeder, B., & Gibson, G. A. (2007). Disk Failures in the Real World: What Does an MTTF of 1,000,000 Hours Mean to You? FAST 2007. 对 LANL 和 HPC 站点超过 10 万块磁盘的故障分析,指出实际故障率是厂商标称的 2 到 10 倍。

  3. Meza, J., Wu, Q., Kumar, S., & Mutlu, O. (2015). A Large-Scale Study of Flash Memory Failures in the Field. SIGMETRICS 2015. Facebook 对其数据中心中数百万块 SSD 的故障统计。

  4. Rashmi, K. V., Shah, N. B., Gu, D., Kuber, H., Borthakur, D., & Ramchandran, K. (2013). A Solution to the Network Challenges of Data Recovery in Erasure-coded Distributed Storage Systems: A Study on the Facebook Warehouse Cluster. HotStorage 2013. Facebook 纠删码修复流量优化的实践。

  5. Huang, C., Simitci, H., Xu, Y., Ogus, A., Calder, B., Gopalan, P., Li, J., & Yekhanin, S. (2012). Erasure Coding in Windows Azure Storage. USENIX ATC 2012. Azure 存储的纠删码实现,介绍了局部修复码(LRC)的工程应用。

行业报告

  1. Backblaze. Hard Drive Stats. https://www.backblaze.com/cloud-storage/resources/hard-drive-test-data. 自 2013 年起持续发布的硬盘故障率统计数据。

官方复盘与文档

  1. GitLab. (2017). GitLab.com Database Incident. https://about.gitlab.com/blog/2017/02/01/gitlab-dot-com-database-incident/. GitLab 数据库删除事件的官方复盘。

  2. AWS. (2017). Summary of the Amazon S3 Service Disruption in the Northern Virginia (US-EAST-1) Region. https://aws.amazon.com/message/41926/. S3 us-east-1 中断事件的官方总结。

  3. AWS. S3 Storage Classes. https://aws.amazon.com/s3/storage-classes/. S3 持久性设计的官方说明。

书籍

  1. Kleppmann, M. (2017). Designing Data-Intensive Applications. O’Reilly. 第五章和第七章对复制、容错和一致性有深入讨论。

上一篇: 存储与计算分离架构 下一篇: 存储故障模式

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2025-10-19 · storage

【存储工程】云对象存储内部架构

深入剖析云对象存储——S3的11个9持久性实现、元数据-索引-存储三层架构、跨AZ复制策略、存储类别实现差异与成本模型分析

2025-10-01 · storage

【存储工程】副本与复制策略

深入分析分布式存储中的数据复制——同步/异步/半同步复制、链式复制、多主复制与冲突解决、Quorum 复制的工程实践

2025-09-27 · storage

【存储工程】纠删码原理与存储效率

深入剖析纠删码的数学原理与工程实践——Reed-Solomon 编码、编码矩阵与恢复过程、参数选择、降级读性能、局部修复码,以及在 MinIO/Ceph 中的配置实战

2026-04-22 · db / storage

数据库内核实验索引

汇总本站数据库内核与存储引擎实验文章,重点覆盖从零实现 LSM-Tree 及其工程权衡。


By .