本文记录中科大研究生课程——《存储与文件系统》的EC实验。使用Jerasure纠删码函数库实现经典的EC算法。
实验介绍
Jerasure是一个C/C++纠删码函数库,提供了编解码过程中可直接调用的工具函数。本次实验要求将多个经典的EC编码实现在Jerasure库中。
可使用函数:
1 | int *cauchy_good_general_coding_matrix(int k, int m, int w) |
实验环境
| CPU | Intel(R) Xeon(R) Silver 4110 CPU @ 2.10GHz |
|---|---|
| 内存 | 4194304 kB |
| 操作系统 | Ubuntu 20.04.3 (64位) |
| C/C++编译器 | gcc version 9.4.0 |
任务三 实现RAID-6编码
任务
- 给定一个大小为1GB的文件的路径,生成由(4,2) RAID-6编码得到的数据块和校验块文件(块大小:16MB),并计算编码吞吐量;
- 模拟当一个节点故障,修复该节点上的所有块,计算修复的吞吐量。
编码
RAID-6编码采用P+Q两冗余分块以容许任意两磁盘并发同时失效的存储级别。以(4,2) RAID-6为例,一个条带的编码过程可以表示为:

实现步骤:
- 调用Jerasure提供的接口
cauchy_good_general_coding_matrix生成一个m*k的基于有限域的柯西矩阵。
1 | case RAID6: |
- 调用接口
jerasure_matrix_dotprod,将生成的柯西编码矩阵与k个数据块进行乘法运算,最后得到m个校验块。
1 | void raid6_encode(int k, int m, int w, int *matrix, |
解码
当某一数据块不可用时,需要从剩余块中读取4个块,并解码恢复出数据块。例如,当\(D_{i,0}\)不可用时,可以读取\(D_{i,1},D_{i,2},D_{i,3},P_i\)进行解码,计算过程如下:

RAID-6解码过程最核心的就是求出编码矩阵的逆矩阵,与剩余完好的盘块矩阵相乘即可恢复。这里参考了jerasure_matrix_decode接口函数的解码过程,使用lastdrive对数据盘和校验盘分开处理,只有在有坏块且坏块是数据盘才需要生成逆矩阵,对于损坏的校验盘直接重新生成。
实现步骤:
- 根据erasures生成erased数组 记录每个磁盘的状态
- 生成解码矩阵(只有在有坏块且坏块是数据盘才需要生成逆矩阵)
- 根据生成的解码矩阵 解码数据磁盘块
- 重新生成所有剩下的坏磁盘块(此时应该只有校验盘块需要被修复)
解码函数如下:
1 | int raid6_decode(int k, int m, int w, int *matrix, int row_k_ones, int *erasures, |
测试
- 使用随机生成的矩阵模拟磁盘数据,测试解码和编码,结果如下:

- 生成指定大小为1G的文件
1 | dd if=./data/test.txt of=./data/test_1G.txt count=1 bs=1G |
查看生成文件大小:
1 | cd ./data |

在Example文件夹下新建文件a_my_encoder.c和a_my_decoder.c,复制encoder.c和decoder.c中的内容到新建的文件中。修改文件输入算法列表,添加RAID-6的编码和解码算法。并将新建的文件添加到Makefile.am文件中,这样就可以生成可执行文件了。


- 编译:在项目根目录下使用make编译。
- 编码:再进入Example文件夹下,使用RAID-6算法对之前生成的1G文件进行编码,在当前目录的Coding文件夹下生成4个数据块文件、2个校验块文件和1个元数据文件。
1 | ./a_my_encoder 'data/test_1G.txt' 4 2 'RAID6' 8 8 1024 |


- 解码:删掉1个数据文件
test_1G_k1.txt,解码出原始文件test_1G_decoded.txt。
1 | ./a_my_decoder 'data/test_1G.txt' |

任务四 实现Rotated RS编码
任务
- 给定一个大小为1.5GB的文件的路径,生成由(6,3) Rotated
RS编码得到的数据块和校验块文件(块大小:16MB),并计算编码吞吐量;
- 模拟当一个数据节点发生故障,修复该节点上的所有块,计算修复的吞吐量;
- 模拟当一个校验节点发生故障,修复该节点上的所有块,计算修复的吞吐量。
编码
Rotated RS和标准的RS码相比减少了降级读和修复时的磁盘IO。Rotated RS编码原理如下:


实现思路:利用上述公式进行编码,将每块磁盘分成 r 块。根据参考论文的实验,(6,3) Rotated RS编码在r=4时效果比较好。
1 | void rotatedrs_encode(int k, int m, int w, char **data_ptrs, char **coding_ptrs, int size) |
解码
当发生磁盘故障,需要对数据进行修复时,部分情况下Rotated RS有着更低的IO开销。如下图所示,当Data Disk 0发生故障时,使用Rotated RS只需要读取16个块,而使用RS需要读取24个块。

实现思路:分别考虑1个数据盘或1个校验盘损坏的情况。
- 在校验盘损坏时,直接使用编码算法重新生成损坏的校验盘。
- 若数据盘损坏,使用上文提到的解码算法,用两个完好的校验盘恢复数据,减少IO开销。
下面详细介绍数据盘的恢复:
因为r=4,需要分别恢复4个磁盘区间的数据,用b=0,1,2,3标记正在恢复的磁盘区间。
- b=0以及b=2区间,只需要通过第一个校验盘与其他数据盘块对应的区间异或即可恢复,以b=0为例:
1 | // 恢复 b=0 此时系数全为1 只要异或即可 |
- b=1以及b=3区间,在异或时不仅需要考虑系数,还需要考虑不同盘块异或区间的改变,以b=1为例:
1 | // 恢复 b=1 此时系数为 2^{i*j} |
测试
- 使用随机生成的矩阵模拟磁盘数据,测试解码和编码,成功恢复了数据。

- 编码
1 | ./a_my_encoder 'data/test_1G.txt' 6 3 'Rotate_RS' 8 8 1024 |

- 解码(数据盘)
删除文件test_1G_k1.txt
1 | ./a_my_decoder 'data/test_1G.txt' |

- 解码(校验盘)
删除编码文件,重新生成后再删除文件test_1G_m2.txt,解码结果如下:
