如何选择纠删码编码引擎 | 纠删码技术详解(上)

做者介绍:
徐祥曦,七牛云工程师,独立开发了多套高性能纠删码/再生码编码引擎。
柳青,华中科技大学博士,研究方向为基于纠删码的分布式存储系统。git


前言:
随着数据的存储呈现出集中化(以分布式存储系统为基础的云存储系统)和移动化(互联网移动终端)的趋势,数据可靠性愈发引发你们的重视。集群所承载的数据量大大上升,但存储介质自己的可靠性进步却很小,这要求咱们必须以更加经济有效的方式来保障数据安全。github

副本与纠删码都是经过增长冗余数据的方式来保证数据在发生部分丢失时,原始数据不发生丢失。但相较于副本,纠删码能以低得多的存储空间代价得到类似的可靠性。好比 3 副本下,存储开销为 3,由于一样的数据被存储了三份,而在 10+3(将原始数据分为 10 份,计算 3 份冗余)的纠删码策略下,存储开销为为 1.3。采用纠删码可以极大地减小存储系统的存储开销,减小硬件、运维和管理成本,正是这样巨大的收益驱使各大公司纷纷将纠删码应用于本身的存储系统,好比 Google、Facebook、Azure、EMC 等等国际巨头,在国内以淘宝、华为、七牛云等为表明的公司也在本身的存储系统上应用了纠删码。算法

最典型的纠删码算法是里德-所罗门码(Reed-Solomon 码,简称 RS 码)。RS 码最先应用于通讯领域,通过数十年的发展,其在存储系统中获得普遍应用,好比光盘中使用 RS 码进行容错,防止光盘上的划痕致使数据不可读;生活中常用的二维码就利用了RS 码来提升识别的成功率。近年 RS 码在分布式存储系统中的应用被逐渐推广,一方面是分布式存储系统存储的存储容量和规模增大的需求;另外一方面是因为纠删码编码速度在近年获得迅猛提高。随着对高性能纠删码引擎在实际系统中应用须要,也催生了对纠删码在具体系统中实现的各类优化手段。并为相关的决策者带来了困扰——究竟什么样的编码引擎才是高效的呢?数组

咱们将以这个问题展开对纠删码技术的剖析,帮助企业更全面,深刻的了解纠删码在存储系统中的应用并更好地作出技术选型。本系列文章将从纠删码的基本原理开始,随后引出如何判断编码引擎优劣这个问题,接下来将深度分析代码实现,帮助开发者顺利完成定制开发。缓存

本系列共计上下两篇篇文章:安全

(上篇)如何选择纠删码编码引擎网络

(下篇)实现高性能纠删码引擎运维

本文做为系列首篇,咱们将一块儿探讨纠删码的编码原理与如何选择编码引擎这两个问题。分布式

一 、纠删码编码原理

在展开分析以前,咱们先来看一看 RS 码是如何工做的。post

下图展现了 3+2(3 份数据,2 份冗余)下对 2 字节长度的数据进行编码与数据修复过程:

为了计算冗余数据,首先咱们须要选举出一个合适的编码矩阵。编码矩阵的上部为一个单位矩阵,这样保证了在编码后原始数据依然能够直接读取。经过计算编码矩阵和原始数据的乘积,能够到最终的结果。

下面介绍解码过程,当 1,2 两块数据丢失,即:

当数据块发生丢失,在编码矩阵中去掉相应行,等式仍然保持成立。这为咱们接下来恢复原始数据提供了依据。

原始数据的修复过程以下:

为了恢复数据,首先咱们求剩余编码数据的逆矩阵,等式两边乘上这个逆矩阵仍然保持相等。与此同时,互逆矩阵的乘积为单位矩阵,所以能够被消掉。那么所求得的逆矩阵与剩余块的数据的乘积就是原始数据了。

数据编码以字节为单位,若是将被编码数据看作一个「数组」,「数组」中每一个元素是一个字节,数据按照字节顺序被编码。编码过程是计算编码矩阵中元素和「数组」的乘积过程。为保证乘积的运算结果仍旧在一个字节大小之内(即 0-255),必须应用到有限域[1]。有限域上的算术运算不一样于一般实数的运算规则。咱们一般事先准备好乘法表,并在算术运算时对每一次乘法进行查表获得计算结果。早期的编码引擎之因此性能不佳,是由于逐字节查表的性能是很是低的。假若能一次性对多字节进行查表以及相应的吞吐和运算,引擎的工做效率必将大幅度提高。

许多 CPU 厂商提供了包含更多位数的寄存器(大于 64 位),这类寄存器和相应支持的运算使得用户程序能够同时对大于机器位数的数据进行运算,支持这类寄存器和运算的指令称之为SIMD(Single Instruction Multiple Data)指令集,好比 Intel 支持的 SSE 指令集最大支持 128 bits 的数据运算,AVX2 指令集最大支持 512 bits 的数据运算。它们为咱们对一个「数组」数据分别执行相同的操做,提升了数据运算的并行性。目前,市面上全部高性能的纠删码引擎均采用了该项技术以提升编解码性能。

2、编码引擎评判标准

咱们将从如下几个关键指标来对编码引擎进行分析:

一、 高编/解码速度;

二、参数可配置;

三、编码速度稳定性;

四、代码简洁、稳定;

5 、下降修复开销等。

2.1 高编/解码速度

上文提到,依赖于SIMD 技术 RS 码编码性能有了大幅度的提升。其中,咱们能够利用多种指令集扩展以供加速,引擎应该能自动根据 CPU 的特性而选择最优的指令集扩展进行加速。

速度是最基本的要求。不过在这里我很难给出一个绝对的数字来衡量速度,由于其受参数,运行平台的影响极大。在下文中提到的三款引擎均有出色的性能表现,能够以它们为基准来衡量引擎的编码速度。除此以外,咱们还能够将逐字节查表(下称基本方法)的编码速度与利用 SIMD 技术加速的编码速度作对比,二者之间应该有很是直观的差距。以个人我的电脑为例(i5-4278U 2.6GHz),在 10+4 的策略下(每一个数据块大小为 128KB),基本方法的速度为(原始数据总量/编码耗时)318.1 MB/s,而经过 AVX2 指令集加速后达到了 5558.6 MB/s[2],在 SSSE3 指令集的加速下也有 2978.87 MB/s 。

另外,解码速度应该大于或等于编码速度(视丢失的数据块数量而定),下图截自在我本机上运行的修复原始数据块的性能测试结果:

2.2 参数可配置

一款合理的纠删码引擎必须能作到编码策略在理论范围内可随意切换,这指的是若是要将编码策略进行变化时,仅需从接口传入不一样参数而不须要改动引擎自己。这大大下降了后续的开发和维护所须要的精力。一个可配置参数的编码引擎能够根据数据的冷热程度和数据重要程度选择不一样的编码系数,好比可靠性要求高的数据能够选择更多冗余。

2.3 编码速度稳定性

速度的稳定性指的是对于不一样尺寸的数据块会有相近的性能表现。因为系统缓存的影响,当被编码数据的大小和缓存大小至关时,编码应该具备最快的速度。当编码数据的大小大于缓存大小时,内存带宽成为编码速度的瓶颈,文件大小和编码时间呈现近似线性关系。这样,数据编码时间是可预期的,用户的服务质量也是可保障的。在实际中,咱们对于大文件进行定长分块,依次编码,分块大小和缓存大小保持必定关系:以 10+4 编码方法为例,对比数据块尺寸分别为取 L3 Cache Size 的 1/12 以及 12 倍。如 L3 Cache Size 的大小为 12MB,则每一块的数据尺寸分别取 1MB,144MB。假若大数据块下编码速度远远低于小数据块,则说明该引擎 CPU cache 的优化工做作得不充分。对于上述参数来讲,大数据块的速度应该不低于小数据块的 70% 。一样以个人我的电脑为例(L3 Cache 大小为 3MB):

2.4 代码简洁、稳定

为了利用 SIMD 加速咱们不得不引入汇编代码或者封装后的 CPU 指令,所以代码形式并不常见。为了加强可读性可将部分逻辑抽离到高级语言,然而会损失部分性能,这其中的利弊须要根据团队的研发实力进行权衡。

接下来的可维护性也很是重要。首先是接口稳定,不会随着新技术的引入而致使代码大规模重构;另外代码必须通过有合理的测试模块以便在后续的更新中校验新算法。

好比早先的 SIMD 加速是基于 SSE 指令集扩展来作的,随后 Intel 又推出 AVX 指令集进一步提升了性能,引擎应该能即时跟上硬件进步的步伐。在比方说,再生码(能够理解为能减小修复开销的纠删码)是未来发展的趋势,但咱们不能由于算法的升级而随意改变引擎的接口。

2.5 下降修复开销

纠删码的一大劣势即是修复代价数倍于副本方案。k+m 策略的 RS 码在修复任何一个数据块时,都须要k 份的其余数据从磁盘上读取和在网络上传输。好比 10+4 的方案下,丢失一个数据块将必须读取 10 个块来修复,这个修复过程占用大量磁盘 I/O 和网络流量,并使得系统暴露在一种降级的不稳定状态。所以,实际系统中应该尽可能避免使用过大的 k 值。

再生码[2] 即是为了缓解数据修复开销而被提出的,它可以极大减小节点失效时所须要的吞吐的数据量。然而其复杂度大,一方面下降了编码速度,另一方面牺牲了传统 RS 码的一些优秀性质,在工程实现上的难度也大于传统纠删码。

3、著名引擎对比

目前被应用最普遍并采用了 SIMD 加速的引擎有以下几款:

  1. Intel 出品的 ISA-L[4]

  2. J.S.Plank 教授领导的 Jerasure[4]

  3. klauspost 的我的项目(in Golang)[6]

这三款引擎的执行效率都很是高,在实现上略有出入,如下是具体分析:

3.1 ISA-L

纠删码做为 ISA-L 库所提供的功能之一,其性能应该是目前业界最佳。须要注意的是 Intel 采用的性能测试方法与学术界经常使用的方式略有出路,其将数据块与冗余块的尺寸之和除以耗时做为速度,而通常的方法是不包含冗余块的。另外,ISA-L 未对 vandermonde 矩阵作特殊处理,而是直接拼接单位矩阵做为其编码矩阵,所以在某些参数下会出现编码矩阵线性相关的问题。好在 ISA-L 提供了cauchy 矩阵做为第二方案。

ISA-L 之因此速度快,一方面是因为 Intel 谙熟汇编优化之道,其次是由于它将总体矩阵运算搬迁到汇编中进行。但这致使了汇编代码的急剧膨胀,使人望而生畏。

另外 ISA-L 支持的指令集扩展丰富,下至 SSE,上到 AVX512,平台适应性最强。

3.2 Jerasure2.0

不一样于 ISA-L 直接使用汇编代码,Jerasure2.0 使用 C 语言封装后的指令,这样代码更加的友好。另外 Jerasure2.0 不只仅支持 GF(2^8) 有限域的计算,其还能够进行 GF(2^4) - GF(2^128) 之间的有限域。而且除了 RS 码,还提供了 Cauchy Reed-Solomon code (CRS 码)等其余编码方法的支持。它在工业应用以外,其学术价值也很是高。目前其是使用最为普遍的编码库之一。目前 Jerasure2.0 并不支持 AVX 加速,尽管如此,不过在仅使用 SSE 的状况下,Jerasure2.0 依然提供了很是高的性能表现。不过主要做者之一 James S. Plank 教授转了研究方向,另一位做者 Greenan 博士早已加入工业界。所以后续的维护将是个比较大的问题。

3.3 klauspost 的 ReedSolomon

klauspost 利用 Golang 的汇编支持,友好地使用了 SIMD 技术,此款引擎的 SIMD 加速部分是目前我看到的实现中最为简洁的,矩阵运算的部分逻辑被移到了外层高级语言中,加上 Golang 自带的汇编支持,使得汇编代码阅读起来更佳的友好。不过 Go 并无集成全部指令,部分指令不得不利用 YASM 等汇编编译器将指令编译成字节序列写入汇编文件中。一方面致使了指令的彻底不可读,另一方面这部分代码的语法风格是 Intel 而非 Golang 汇编的 AT&T 风格,平添了迷惑。这款引擎比较明显的缺陷有两点:1.对于较大的数据块,编码速度会有巨大的下滑;2.修复速度明显慢于编码速度。

4、本身实现一款引擎

多是因为对开源库后续维护问题的担心,也有多是现有方案并不能知足企业对某些特定需求和偏好,不少公司选择了自研引擎。那么如何写出高效的代码呢?在上面的简单介绍中,受限于篇幅我跳过了不少细节。好比 SIMD 技术是如何为纠删码服务的,以及如何利用 CPU Cache 作优化等诸多重要问题。咱们会在后续的文章中逐步展开其实现,欢迎你们继续关注。


附录:

  1. 许以超 马松雅. 代数编码与密码[M]. 北京:高等教育出版社, 2015.
  2. 徐祥曦 Reed-Solomon

  3. Alexandros G Dimakis, P Godfrey, Yunnan Wu, Martin J Wainwright, and Kannan Ramchan-dran. Network coding for distributed storage systems. Information Theory, IEEE Transactions on, 56(9):4539–4551, 2010.

  4. Intel ISA-L

  5. Jerasure

  6. klauspost Reed-Solomon

相关文章
相关标签/搜索