文件数据块分块算法解析

1. 简介
       存储系统的重复数据删除过程一般是这样的:首先将数据文件分割成一组数据块,为每个数据块计算指纹,然后以指纹为关键字进行Hash查找,匹配则表示该数据块为重复数据块,仅存储数据块索引号,否则则表示该数据块是一个新的唯一块,对数据块进行存储并创建相关元信息。这样,一个物理文件在存储系统就对应一个逻辑表示,由一组FP组成的元数据。当进行读取文件时,先读取逻辑文件,然后根据FP序列,从存储系统中取出相应数据块,还原物理文件副本。从如上过程中可以看出,存储系统的重复数据删除的关键技术主要包括文件数据块切分、数据块指纹计算和数据块检索。
2.数据块分块算法
2.1 定长分块(fixed-size partition)
       定长分块算法采用预先义好的块大小对文件进行切分,并进行弱校验值和md5强校验值。弱校验值主要是为了提升差异编码的性能,先计算弱校验值并进行hash查找,如果发现则计算md5强校验值并作进一步hash查找。由于弱校验值计算量要比md5小很多,因此可以有效提高编码性能。定长分块算法的优点是简单、性能高,但它对数据插入和删除非常敏感,处理十分低效,不能根据内容变化作调整和优化.
下面的chunk_file_hdr和chunk_block_entry数据结构见博客: http://blog.csdn.net/chenglinhust/article/details/8776830。
代码为:
[cpp]  view plain copy print ?
  1. /* fix-sized file chunking */  
  2. /*fd_src为分块的源文件*/  
  3. /*fd_chunk为分块后的文件*/  
  4. /*分块文件头chunk file header*/  
  5. static int file_chunk_fsp(int fd_src, int fd_chunk, chunk_file_header *chunk_file_hdr)  
  6. {  
  7.     unsigned int rwsize;  
  8.     unsigned char md5_checksum[16 + 1] = {0};  
  9.     unsigned char csum[10 + 1] = {0};  
  10.     char buf[BLOCK_SZ] = {0};  
  11.     chunk_block_entry chunk_bentry; //一个chunk block entry  
  12.     uint64_t offset = 0;  
  13.   
  14.   
  15.     while (rwsize = read(fd_src, buf, BLOCK_SZ)) {  
  16.         md5(buf, rwsize, md5_checksum);  
  17.         uint_2_str(adler32_checksum(buf, rwsize), csum);  
  18.         chunk_bentry.len = rwsize;  
  19.         chunk_bentry.offset = offset;  
  20.         memcpy(chunk_bentry.md5, md5_checksum, 16 + 1);  
  21.         memcpy(chunk_bentry.csum, csum, 10 + 1);  
  22.         rwsize = write(fd_chunk, &chunk_bentry, CHUNK_BLOCK_ENTRY_SZ);  
  23.         if (rwsize == -1 || rwsize != CHUNK_BLOCK_ENTRY_SZ)  
  24.             return -1;  
  25.   
  26.         offset += rwsize;  
  27.         chunk_file_hdr->block_nr++;  
  28.     }  
  29.     if (rwsize == -1)  
  30.         return -1;  
  31.   
  32.     return 0;  
  33. }  
2.2 CDC切分(content-defined chunking)
      CDC(content-defined chunking)算法是一种变长分块算法,它应用数据指纹(如Rabin指纹)将文件分割成长度大小不等的分块策略。与定长分块算法不同,它是基于文件内容进行数据块切分的,因此数据块大小是可变化的。算法执行过程中,CDC使用一个固定大小(如48字节)的滑动窗口对文件数据计算数据指纹。如果指纹满足某个条件,如当它的值模特定的整数等于预先设定的数时,则把窗口位置作为块的边界。CDC算法可能会出现病态现象,即指纹条件不能满足,块边界不能确定,导致数据块过大。实现中可以对数据块的大小进行限定,设定上下限,解决这种问题。CDC算法对文件内容变化不敏感,插入或删除数据只会影响到检少的数据块,其余数据块不受影响。CDC算法也是有缺陷的,数据块大小的确定比较困难,粒度太细则开销太大,粒度过粗则dedup效果不佳。如何两者之间权衡折衷,这是一个难点。
代码为:
[cpp]  view plain copy print ?
  1. /* content-defined chunking */  
  2. /*fd_src为分块的源文件*/  
  3. /*fd_chunk为分块后的文件*/  
  4. /*分块文件头chunk file header*/  
  5. static int file_chunk_cdc(int fd_src, int fd_chunk, chunk_file_header *chunk_file_hdr)  
  6. {  
  7.     char buf[BUF_MAX_SZ] = {0};  //缓冲区最大值  
  8.     char block_buf[BLOCK_MAX_SZ] = {0}; // 块的最大值  
  9.     char win_buf[BLOCK_WIN_SZ + 1] = {0}; //块的窗口大小  
  10.     unsigned char md5_checksum[16 + 1] = {0};  
  11.     unsigned char csum[10 + 1] = {0};  
  12.     unsigned int bpos = 0;  
  13.     unsigned int rwsize = 0;  
  14.     unsigned int exp_rwsize = BUF_MAX_SZ;  
  15.     unsigned int head, tail;  
  16.     unsigned int block_sz = 0, old_block_sz = 0;  
  17.     unsigned int hkey = 0;  
  18.     chunk_block_entry chunk_bentry; //分块实体  
  19.     uint64_t offset = 0;  
  20.   
  21.     while(rwsize = read(fd_src, buf + bpos, exp_rwsize)) {  
  22.         /* last chunk */  
  23.         if ((rwsize + bpos + block_sz) < BLOCK_MIN_SZ)  
  24.             break;  
  25.   
  26.         head = 0;  
  27.         tail = bpos + rwsize;  
  28.         /* avoid unnecessary computation and comparsion */  
  29.         if (block_sz < (BLOCK_MIN_SZ - BLOCK_WIN_SZ)) {  
  30.             old_block_sz = block_sz;  
  31.             block_sz = ((block_sz + tail - head) > (BLOCK_MIN_SZ - BLOCK_WIN_SZ)) ?  
  32.                 BLOCK_MIN_SZ - BLOCK_WIN_SZ : block_sz + tail -head;  
  33.             memcpy(block_buf + old_block_sz, buf + head, block_sz - old_block_sz);  
  34.             head += (block_sz - old_block_sz);  
  35.         }  
  36.   
  37.         while ((head + BLOCK_WIN_SZ) <= tail) {  
  38.             memcpy(win_buf, buf + head, BLOCK_WIN_SZ);  
  39.             hkey = (block_sz == (BLOCK_MIN_SZ - BLOCK_WIN_SZ)) ? adler32_checksum(win_buf, BLOCK_WIN_SZ) :  
  40.                 adler32_rolling_checksum(hkey, BLOCK_WIN_SZ, buf[head-1], buf[head+BLOCK_WIN_SZ-1]);  
  41.   
  42.             /* get a normal chunk, write block info to chunk file */  
  43.             if ((hkey % BLOCK_SZ) == CHUNK_CDC_R) {  
  44.                 memcpy(block_buf + block_sz, buf + head, BLOCK_WIN_SZ);  
  45.                 head += BLOCK_WIN_SZ;  
  46.                 block_sz += BLOCK_WIN_SZ;  
  47.                 if (block_sz >= BLOCK_MIN_SZ) {  
  48.                     md5(block_buf, block_sz, md5_checksum);  
  49.                     uint_2_str(adler32_checksum(block_buf, block_sz), csum);  
  50.                     chunk_file_hdr->block_nr++;  
  51.                     chunk_bentry.len = block_sz;  
  52.                     chunk_bentry.offset = offset;  
  53.                     memcpy(chunk_bentry.md5, md5_checksum, 16 + 1);  
  54.                     memcpy(chunk_bentry.csum, csum, 10 + 1);  
  55.                     rwsize = write(fd_chunk, &chunk_bentry, CHUNK_BLOCK_ENTRY_SZ);  
  56.                     if (rwsize == -1 || rwsize != CHUNK_BLOCK_ENTRY_SZ)  
  57.                         return -1;  
  58.                     offset += block_sz;  
  59.                     block_sz = 0;  
  60.                 }  
  61.             } else {  
  62.                 block_buf[block_sz++] = buf[head++];  
  63.                 /* get an abnormal chunk, write block info to chunk file */  
  64.                 if (block_sz >= BLOCK_MAX_SZ) {  
  65.                     md5(block_buf, block_sz, md5_checksum);  
  66.                     uint_2_str(adler32_checksum(block_buf, block_sz), csum);  
  67.                     chunk_file_hdr->block_nr++;  
  68.                     chunk_bentry.len = block_sz;  
  69.                     chunk_bentry.offset = offset;  
  70.                     memcpy(chunk_bentry.md5, md5_checksum, 16+1);  
  71.                     memcpy(chunk_bentry.csum, csum, 10 + 1);  
  72.                     rwsize = write(fd_chunk, &chunk_bentry, CHUNK_BLOCK_ENTRY_SZ);  
  73.                     if (rwsize == -1 || rwsize != CHUNK_BLOCK_ENTRY_SZ)  
  74.                         return -1;  
  75.                     offset += block_sz;  
  76.                     block_sz = 0;  
  77.                 }  
  78.             }  
  79.   
  80.             /* avoid unnecessary computation and comparsion */  
  81.             if (block_sz == 0) {  
  82.                 block_sz = ((tail - head) > (BLOCK_MIN_SZ - BLOCK_WIN_SZ)) ?  
  83.                     BLOCK_MIN_SZ - BLOCK_WIN_SZ : tail - head;  
  84.                 memcpy(block_buf, buf + head, block_sz);  
  85.                 head = ((tail - head) > (BLOCK_MIN_SZ - BLOCK_WIN_SZ)) ?  
  86.                     head + (BLOCK_MIN_SZ - BLOCK_WIN_SZ) : tail;  
  87.             }  
  88.         }  
  89.   
  90.         /* read expected data from file to full up buf */  
  91.         bpos = tail - head;  
  92.         exp_rwsize = BUF_MAX_SZ - bpos;  
  93.         memmove(buf, buf + head, bpos);  
  94.     }  
  95.   
  96.     if (rwsize == -1)  
  97.         return -1;  
  98.     /* process last block */  
  99.     uint32_t last_block_sz = ((rwsize + bpos + block_sz) >= 0) ? rwsize + bpos + block_sz : 0;  
  100.     char last_block_buf[BLOCK_MAX_SZ] = {0};  
  101.     if (last_block_sz > 0) {  
  102.         memcpy(last_block_buf, block_buf, block_sz);  
  103.         memcpy(last_block_buf + block_sz, buf, rwsize + bpos);  
  104.         md5(last_block_buf, last_block_sz, md5_checksum);  
  105.         uint_2_str(adler32_checksum(last_block_buf, last_block_sz), csum);  
  106.         chunk_file_hdr->block_nr++;  
  107.         chunk_bentry.len = last_block_sz;  
  108.         chunk_bentry.offset = offset;  
  109.         memcpy(chunk_bentry.md5, md5_checksum, 16+1);  
  110.         memcpy(chunk_bentry.csum, csum, 10 + 1);  
  111.         rwsize = write(fd_chunk, &chunk_bentry, CHUNK_BLOCK_ENTRY_SZ);  
  112.         if (rwsize == -1 || rwsize != CHUNK_BLOCK_ENTRY_SZ)  
  113.             return -1;  
  114.     }  
  115.   
  116.     return 0;  
  117. }  
这个算法简单解释下:先读取一个BUF_MAX_SZ的字节流,这对这些字节流进行操作。每次移动BLOCK_WIN_SZ个字节,然后进行hash,达到指定的条件再向右移动BLOCK_WIN_SZ,当达到BLOCK_MIN_SZ和BLOCK_MAX_SZ之间的时候,就可以当做一个块。如果不满足条件,每次移动一个字节,直到达到边界条件。
2.3 滑动块切分(sliding block)
      滑动块(sliding block)算法结合了定长切分和CDC切分的优点,块大小固定。它对定长数据块先计算弱校验值,如果匹配则再计算md5强校验值,两者都匹配则认为是一个数据块边界。该数据块前面的数据碎片也是一个数据块,它是不定长的。如果滑动窗口移过一个块大小的距离仍无法匹配,则也认定为一个数据块边界。滑动块算法对插入和删除问题处理非常高效,并且能够检测到比CDC更多的冗余数据,它的不足是容易产生数据碎片。

你可能感兴趣的:(文件数据块分块算法解析)