数据重删------定长重删和不定长(滑动窗口,内容分块)重删算法

块级重删是指将数据流或者文件根据某种方式进行切片(也叫分块),以数据块为单位进行hash计算,以此找到相同的数据块进行删除。块级重删又分为定长重删和变长重删。

定长重删

定长重删其实很好理解,实现逻辑也比较简单。将要备份的数据以固定长度进行分块,并计算每个分块的hash值(value)。

如下图所示,每4个数据进行分块,数据总共被分成了4个切片,计算各个切片的hash值。当数据首次备份时,得到fingerprint1fingerprint2fingerprint3fingerprint4共4个hash值,这些指纹和数据一起被保存。

数据重删------定长重删和不定长(滑动窗口,内容分块)重删算法_第1张图片

数据的变化分成两种:覆盖写和新增写。

首先来看覆盖写的情况,例如deab --> ddab,整个数据就第二个切片数据发生了变化,因此当第二次备份时只要重新计算第二个切片数据的指纹就可以了,这种情况下重删效果较好。

数据重删------定长重删和不定长(滑动窗口,内容分块)重删算法_第2张图片

但生产数据常常出现在中间位置进行新增和删除数据的情况,这种情况下,定长重删的效果就非常不理想。如下图所示,第二个切片中插入了数据“b”,这就导致整个数据串重新切片,且所有数据分块的hash值相比首次备份都无法匹配,重删率几乎等于0。

数据重删------定长重删和不定长(滑动窗口,内容分块)重删算法_第3张图片

因此,根据不同的场景,定长重删的效果差异很大。一般来讲,定长重删的方式更适用于卷备份、虚拟机备份等场景,这些场景下备份对象的数据长度基本很少发生变化。解决定长重删的问题,就是使用变长分块。目前比较常见的变长分块方案有2种:滑动窗口分块和基于内容分块(CDC:content-defined chunkingcontent-defined chunking)。

滑动窗口分块

基于滑动窗口分块方案的首次备份与定长重删的方法一致,它选用固定的长度对整串数据进行分块,并计算各个分块的hash值。选用的这个固定的长度就是窗口的长度,如下图:

数据重删------定长重删和不定长(滑动窗口,内容分块)重删算法_第4张图片

二次备份的时候,利用窗口的滑动来尝试寻找和匹配相同的数据。

先看数据修改的例子,如下图示,第二个切片发生了数据变化deab --> ddab。首先计算ddab的hash值,该切片的数据是发生了变化的因此无指纹可以匹配。这个时候不着急处理下一个数据切片,而是将窗口向前移动一个单位,继续计算这个窗口下的数据的hash值(fingerprint2’)并尝试匹配,以此类推,直到找到可以匹配的hash值为止。

当某个数据发生覆盖写的时候,其效果与定长重删效果一样,可以获得一样的重删率。

数据重删------定长重删和不定长(滑动窗口,内容分块)重删算法_第5张图片

再来看下当插入新数据时是如何处理的。如下图示,第二个切片数据变化deab --> ddeab。

此时按照上面讲的方法,窗口不断向后滑动,直到窗口滑动到ceab才有指纹向匹配。此时可以看到产生一个新的数据切片(仅包含一个"b");这种新增场景下仅有2个数据切片因为hash值不匹配而需要重新存储,大部分的数据分块得以重删。此种场景下,重删效果相比定长重删要好很多。

数据重删------定长重删和不定长(滑动窗口,内容分块)重删算法_第6张图片

根据上面的过程我们可以总结出以下几点:

  1. 如果在窗口内找到匹配的hash值,那么就可以认为这个窗口内的数据分块是重复且可以删除的,下一次的窗口可以直接从当前这个数据分块的末尾开始,以此提升计算效率。
  2. 如果窗口持续滑动(滑动长度 >= 窗口的长度)而找不到匹配的hash值,说明产生变化的数据量比较大,这种情况下同样以窗口的长度对数据进行分块。
  3. 如果窗口滑动后,很快找到了匹配的hash值(滑动长度 < 窗口长度),那么就将窗口滑过的部分进行单独分块。综合第2点可以得出:分块的长度<=窗口长度

滑动窗口的方案不仅仅应用于重删,同样应用于rsync增量同步、文件查重等场景。此方案可以带来良好的查重效果,但是也引入了“数据碎片”的缺陷。下图中的数据分块“b”即是碎片。碎片如果过多最终会影响整体效率。

数据重删------定长重删和不定长(滑动窗口,内容分块)重删算法_第7张图片

CDC分块

在讲解CDC分块前,可以看我之前的文章了解下Rabin指纹。Rabin指纹是一种高效的指纹计算函数,利用hash函数的随机性,它对任意数据的计算结果表现出均匀分布。其原理大致是这样子的:假设有一串固定长度的数据S,有一个hash计算函数h(),针对S进行hash计算并对固定值D取余,即hash(S) mod D = R。由于hash值的随机性,最终 R会在一定的[0,D)的区间内均匀分布。基于文件内容的分快就是利用了这样的技术,设置一个固定长度的滑动窗口、一个固定值D和一个固定值R(R

假设滑动窗口的固定长度为4,当窗口内的数据串末位为数据“d”时,满足条件 hash(“窗口内的数据串”) mod D = R。初始数据块如下:

窗口从第初始位置滑动。

  1. 初始位置为“abcd”,末位为d,满足 hash("abcd") mod D = R。因此可以认为产生了一个切片边界。
  2. 下一次滑动从边界后开始。"adea"不满足条件,向前滑动一个单位;“dead”满足条件,产生新的切片边界。那么新的切片就是两个边界之间的数据“adead”。
  3. 以此类推,直到整个数据串滑动完为止。滑动结束,数据串的分块也就结束了。

根据上述的分块方式,最终可以得出首次备份的数据串分块如下图。共分为4个数据分块,针对这5个数据分块生成hash值,fingerprint1fingerprint2fingerprint3fingerprint4

数据重删------定长重删和不定长(滑动窗口,内容分块)重删算法_第8张图片

如果二次备份出现数据覆盖写变化的情况,那么影响范围最多只影响2个分块(变化的切片以及其后面的一个数据切片)。如下图,第2个数据切片产生数据变化仅影响其数据切片本身,重删效果较好。

数据重删------定长重删和不定长(滑动窗口,内容分块)重删算法_第9张图片

 

如果二次备份出现新增数据的情况。如下图所示,新增数据不影响数据切片方式,需要备份的变化的数据切片。此外没有产生数据碎片,相比滑动窗口分块方案要更具优势。

数据重删------定长重删和不定长(滑动窗口,内容分块)重删算法_第10张图片

CDC方案实际在使用过程中容易出现问题,如果固定值D、R和窗口长度设置不当,容易出现一些极端情况。比如:滑动过程中始终找不到切片边界,导致数据切片过大。再比如:滑动过程每次都匹配上了,且窗口设置较小,导致切片过多。要解决这些问题,有以下几个方案可供参考:

  1. 消除过小切片:设置切片的最小长度,如果在最小长度内遇到切片边界,则忽略此边界。
  2. 消除过大切片:设置切片的最大长度,如果在最大长度内未找到切片边界,则直接使用最大长度切片。
  3. 双因子切片:设置2组固定值进行Rabin指纹计算,此种方式一般是配合切片最小长度和最大长度使用,在长度区间内选择可用的指纹。

你可能感兴趣的:(存储)