xp的分布式系统系列教程之: Erasure Code: 介绍, 数学原理, 实践.
问题: 分布式系统中通过数据冗余保证可靠性.
多副本
副本数一般是3
d1, d1, d1 d2, d2, d2 ...
冗余度是 300% // 空间浪费200%
能否用较少的冗余, 来实现较高的可靠性?
可否多存储一个数字, 使得整个系统在丢失任何一个数字的时候都可以将其找回?
3个数字, d1, d2, d3
再存储一个值, 是3个数的和: c1 = d1 + d2 + d3
这样如果d1, d2, d3 任意一个丢失, 都可以通过总和c1 减去存在的2个得到.
d1 = c1 - d2 -d3
如果c1丢失, 则再次取 d1, d2, d3 的和就可以将 c1 找回.
在上面这个简单的系统中, 总共存储了4份数据, 有效的数据是3份. 整个系统允许丢失一份数据1/4.
在数学上, 上面这个例子是和我们平时使用的RAID 5 一样的. k个数据, 存储1份校验和, 允许1份数据丢失后可以找回.
当然在工程上, RAID5的计算和纯数学还有些差异. 后面讲.
让我们把问题再推进1步.
d1, d2, d3 可否增加2个冗余的存储, 让整个系统任意丢失2份数据时都能找回?
简单的把 c1 存2次是不够的:
d1 + d2 + d3 = c1
d1 + d2 + d3 = c2
(这里显然c1 == c2)
如果d1, d2都丢失了, x1, x2表示丢失的 d1, d2, 是我们要找回的数字. 下面这个x1 x2的线性方程是有无穷多解的:
x1 + x2 + d3 = c1
x1 + x2 + d3 = c2
我们没有办法从这个方程组里解出x1, x2的值, 因为实际上2个方程是一样的, 没有提供更多的信息
所以我们现在需要做的是, 如何设计一个c2的计算方式, 使得当d1, d2丢失时方程有解.
一个简单的思路是:
d1*1 + d2*1 + d3*1 = c1
d1*1 + d2*2 + d3*4 = c2
计算c1时, 对每个数字乘以1, 1, 1, 1 ... 计算c2时, 对每个数字乘以1, 2, 4, 8 ...
这时, 如果d1, d2丢失:
x1*1 + x2*1 + d3*1 = c1
x1*1 + x2*2 + d3*4 = c2
对x1, x2的找回实际上就是求下面这个线性方程的解:
x1*1 + x2*1 = c1 - d3*1
x1*1 + x2*2 = c2 - d3*4
现在温习一下高中数学, 通过削元就可以解出x1, x2的值啦.
以上实际上就是RAID6的工作方式, 多存储2个校验数据, 当整个系统丢失2个数据时, 都可以找回.
为什么求c2的系数是1, 2, 4, 8..? 系数的选择有很多中方法, 1, 2, 4, 8是其中一个. 只要保证最终丢失2个数字构成的方程组有解就可以. 这里选择1, 2, 3, 4..也可以.
上面介绍了最简单的数据冗余算法, 也就是RAID5和RAID6的实现原理.
实际上它们都是Erasure Code的特例, 现在我们就把RAID5和RAID6推广到一般的Erasure Code算法.
Erasure Code
有1组数字: d1, d2, d3, d4....dk 另外记录n个校验用的数字 使得这k+n个数字中任意丢失n个都能从剩下的n个中恢复所有的k+n个数字.
n个校验的生成算法如下:
d1*1 + d2*1 + d3*1 + d4*1 + ... + dk*1 = c1
d1*1 + d2*2 + d3*4 + d4*8 + ... + dk*2^(k-1) = c2
d1*1 + d2*3 + d3*9 + d4*27 + ... + dk*3^(k-1) = c3
...
d1*n^0 + d2*n^1 + d3*n^2 + d4*n^3 + ... + dk*n^(k-1) = cn
系数C[i, j] = i^(j-1)
数据恢复时, 假设有m个数字丢失了, m<=n
.
从上面选择c1, c2..cm, m行, 选择丢失的m列, 组成的一次方程组, 求解丢失的数据.
这个系数矩阵, 保证了任意m*m
的子矩阵都是线性无关的, 构成的方程肯定有确定解.
这个矩阵就是著名的 [Vandermonde][Vandermonde] 矩阵.
Erasure Code的几何解释
从另1个方面:
Erasure Code 的校验块:
c[x] = d[k]*x^(k-1) + d[k-1]*x^(k-2) + .. + d3*x^2 + d2*x + d1
x = [1..n]
$$ y = d_7x^6 + d_6x^5 + d_5x^4 + d_4x^3 + d_3x^2 + d_2x + d_1 $$
可以看出这是1个k-1次的曲线的方程, Erasure Code 中第1, 2, 3..个校验值, 就是这个k-1次曲线在x=1, 2, 3...上的y的值.
Erasure Code 可以视为: 通过记录曲线经过的n个点的位置, 来还原曲线. 记录一个k-1次曲线的在x=1, 2, 3..上的点的位置,
以上就是Erasure Code的基本原理.
下面是Erasure Code的实现.
在实际使用中, 并不像纯数学那么简单: 就像所有算法在现实中面临的问题一样, 上面介绍的Erasure Code中有1点是在计算机上必须重新设计的
那就是数字大小的问题.
Erasure Code实现: 有限域, Erasure Code的加法和乘法.
现在将我们的数字d1, d2..放到内存里, 假设每个数字的宽度都是1个8bit的字节. 那么, 最终的c1, 实际上可能会大于1个字节的大小, 导致存储溢出.
这时如果仍然使用这个运算方法, 实际上存储的空间必须要足够大, 导致最终结果不能满足我们的预期, 实际存储的冗余会大于k+n : k
的冗余度.
19世纪因为跟情敌决斗被一枪打死的伟大数学家的理论给出了1个方案.
Erasure Code需要的是通过乘法和加法建立一个方程组来求得丢失的数据. 上面的介绍中使用了自然数的加法和乘法.
但如果不使用我们熟知的自然数的加法和乘法. 但使用另外一种加法和乘法, 保证加法和乘法可逆, 就可以用来建立线性方程.
Erasure Code的加法: 异或
为了使得方程可以通过四则运算可以解, 加法需要满足结合律, 可逆
0 ⊕ 0 = 0
0 ⊕ 1 = 1
1 ⊕ 0 = 1
1 ⊕ 1 = 0
0 和 1 在 ⊕ 运算上构成1个环.
所有的多项式集合 $$ sum_{x} a_ix^i $$
⊗
可靠性和可用性分析
LRC
IO消耗