資料相似性檢測演算法

來源:互聯網
上載者:User

1、引言
  "資料同步演算法研究"一文研究了在網路上高效同步資料的方法,其中有個前提是檔案A和B非常相似,即兩者之間存在大量相同的資料。如果兩個檔案相似性很低,雖然這種方法依然可以正常工作,但資料同步效能卻不會得到提高,甚至會有所降低。因為會產生部分中繼資料和網路通訊消耗,這在兩個檔案完全不相關時尤為明顯。因此,同步資料前需要計算種子檔案(seed file)與目標檔案之間的相似性,如果相似性大於指定閾值(通常應大於50%)則應用該資料同步演算法,否則接傳輸檔案即可。如此,可使得資料同步演算法則具有較好的自適應性,在資料具有不同相似性的情形下均可進行高效能的資料同步。另外,在資料相似性檢測的基礎之上,可對於相似性高的資料進行資料編碼處理(如Delta編碼),通過一個檔案給另一個檔案編碼的方式進行資料壓縮,這是一種基於相似資料檢測與編碼的重複資料刪除技術。
2、相似性計算
  Unix diff對文檔進行逐行對比來檢測相似檔案,它採用經典的LCS(Longest Common Subsequence,最長公用子串)演算法,運用動態規劃方法來計算相似性。LCS的含義是同時包含在字串裡的一個最長字元序列,LCS的長度作為這兩個字串相似性的度量。Diff演算法以整行作為"字元"來計算最長公用子串,效能上比字元級的LCS演算法快很多。這種方法效率很低,而且只適用文字檔的相似比較,不能直接適用於二進位檔案。

  目前通常的做法是將檔案相似性問題轉換為集合相似性問題,如基於shingle的計算方法和基於bloom filter的計算方法,這兩種方法都可適用於任何格式的資料檔案。這種方式的核心思想是為每個檔案提取組特徵值,以特徵值集合來計算相似性,從而降低計算複雜性來提高效能。shingle用特徵值交集來計算相似性會導致高計算和空間開銷,bloom filter技術在計算開銷和匹配精度上更具勢。Bloom filter所定義的集合元素是檔案按照CDC(content-defined chunking)演算法所切分資料區塊的指紋值,其相似性定義如下:
                            |fingerprints(f1) ∩ fingerprints(f2)|
  Sim(f1, f2) = ---------------------------------------------   (公式1)
                            |fingerprints(f1) ∪ fingerprints(f2)|

  另外一種方法,是將二進位檔案進行切塊,使用資料區塊指紋來表示資料區塊,然後將資料區塊映射為"字元",再應用LCS演算法尋找最大公用子串並計算出相似性。其相似性定義如下:
                        2 * length(LCS(fingerprints(f1), fingerprints(f2)))
    Sim(f1, f2) = ------------------------------------------------------------------ (公式2)
                           length(fingerprints(f1)) + length(fingerprints(f2))

  上面兩種相似性演算法中均採用資料切分技術,資料區塊可以是定長或變長。為了相似性計算的精確性,實現中採用以資料區塊長度作為權的加權計算方法。

3、Bloom filter演算法
  該檔案相似性計算流程如下:
      (1) 採用CDC演算法將檔案切分成資料區塊集,並為每個資料區塊計算MD5指紋;
      (2) 計算兩個指紋集合的交集和並集,通過hashtable來實現;
      (3) 按照公式1計算檔案相似性,考慮重複資料區塊和資料區塊長度來提高計算精確度。
      詳細參見附錄bsim源碼中的file_chunk,chunk_file_process和similarity_detect函數實現。

4、LCS演算法
  該檔案相似性計算流程如下:
    (1) 採用CDC演算法將檔案切分成資料區塊集,並為每個資料區塊計算MD5指紋;
    (2) 將MD5指紋串映射為"字元",則檔案轉換為"字串"表示;
    (3) 應用LCS演算法計算出最長公用子串,並計算其加權長度;
    (4) 按照公式2計算檔案相似性,考慮重複資料區塊和資料區塊長度來提高計算精確度。
    詳細參見附錄bsim源碼中的file_chunk,chunk_file_process,LCS和similarity_detect函數實現。

5、演算法分析比較
 兩種演算法都對檔案進行切分操作,假設檔案f1切為m個塊,檔案f2切分成n個塊。Bloom filter演算法沒有考慮資料區塊順序,因此在相似性精確度方面要低於LCS演算法,其時間和空間複雜性都是O(m + n)。相反,LCS演算法考慮了資料區塊順序問題,相似性度量相對精確,然而其時間和空間複雜性是O(mn),這個大大限制了應用規模。綜合來看,Bloom filter演算法精確度比LCS演算法要低,但計算消耗要小很多,效能和適用性非常好。LCS比較適合精確的檔案相似性計算,這些檔案往往比較小,50MB以內比較合適。對於重複資料刪除和網路資料同步來說,消重效果和效能與資料區塊順序性無關,因此Bloom filter演算法計算的資料相似性更適用,效能也更高。

附錄:bsim.c源碼
(完整源碼請參見deduputil源碼)
/* Copyright (C) 2010 Aigui Liu<br /> *<br /> * This program is free software; you can redistribute it and/or modify<br /> * it under the terms of the GNU General Public License as published by<br /> * the Free Software Foundation; either version 3 of the License, or<br /> * (at your option) any later version.<br /> *<br /> * This program is distributed in the hope that it will be useful,<br /> * but WITHOUT ANY WARRANTY; without even the implied warranty of<br /> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br /> * GNU General Public License for more details.<br /> *<br /> * You should have received a copy of the GNU General Public License along<br /> * with this program; if not, visit the http://fsf.org website.<br /> */<br />#include <stdio.h><br />#include <stdlib.h><br />#include <string.h><br />#include <sys/types.h><br />#include <sys/stat.h><br />#include <fcntl.h><br />#include <unistd.h><br />#include "hashtable.h"<br />#include "sync.h"<br />#define NEITHER 0<br />#define UP 1<br />#define LEFT 2<br />#define UP_AND_LEFT 3<br />#define MAX(x, y) (((x) > (y)) ? (x) : (y))<br />#define MIN(x, y) (((x) < (y)) ? (x) : (y))<br />#define MD5_LEN17<br />enum {<br />FILE1 = 0,<br />FILE2<br />};<br />enum {<br />LCS_NOT = 0,<br />LCS_YES<br />};<br />typedef struct {<br />uint32_t nr1;<br />uint32_t nr2;<br />uint32_t len;<br />} hash_entry;<br />typedef struct {<br />char **str;<br />uint32_t len;<br />} lcs_entry;<br />static uint32_t sim_union = 0;<br />static uint32_t sim_intersect = 0;<br />static void usage()<br />{<br />fprintf(stderr, "Usage: bsim FILE1 FILE2 CHUNK_ALGO LCS/n/n");<br />fprintf(stderr, "Similarity detect between FILE1 and FILE2 based on block level./n");<br />fprintf(stderr, "CHUNK_ALGO:/n");<br />fprintf(stderr, " FSP - fixed-size partition/n");<br />fprintf(stderr, " CDC - content-defined chunking/n");<br />fprintf(stderr, " SBC - slide block chunking/n/n");<br />fprintf(stderr, "LCS:/n");<br />fprintf(stderr, " LCS_NOT - do not use LCS(longest lommon subsequence) algorithms/n");<br />fprintf(stderr, " LCS_YES - use LCS algorithms/n/n");<br />fprintf(stderr, "Report bugs to <Aigui.Liu@gmail.com>./n");<br />}<br />static int parse_arg(char *argname)<br />{<br />if (0 == strcmp(argname, "FSP"))<br />return CHUNK_FSP;<br />else if (0 == strcmp(argname, "CDC"))<br />return CHUNK_CDC;<br />else if (0 == strcmp(argname, "SBC"))<br />return CHUNK_SBC;<br />else if (0 == strcmp(argname, "LCS_NOT"))<br />return LCS_NOT;<br />else if (0 == strcmp(argname, "LCS_YES"))<br />return LCS_YES;<br />else<br />return -1;<br />}<br />static char **alloc_2d_array(int row, int col)<br />{<br /> int i;<br /> char *p, **pp;<br /> p = (char *)malloc(row * col * sizeof(char));<br /> pp = (char **)malloc(row * sizeof(char *));<br /> if (p == NULL || pp == NULL)<br /> return NULL;<br /> for (i = 0; i < row; i++) {<br /> pp[i] = p + col * i;<br /> }<br /> return pp;<br />}<br />static void free_2d_array(char **str)<br />{<br />free(str[0]);<br />free(str);<br />}<br />static void show_md5_hex(unsigned char md5_checksum[16])<br />{<br /> int i;<br /> for (i = 0; i < 16; i++) {<br /> printf("%02x", md5_checksum[i]);<br /> }<br /> printf("/n");<br />}<br />static int chunk_file_process(char *chunk_file, hashtable *htab, int which, int sim_algo, lcs_entry *le)<br />{<br />int fd, i, ret = 0;<br />ssize_t rwsize;<br />chunk_file_header chunk_file_hdr;<br />chunk_block_entry chunk_bentry;<br />hash_entry *he = NULL;<br />/* parse chunk file */<br />fd = open(chunk_file, O_RDONLY);<br />if (-1 == fd) {<br />return -1;<br />}<br />rwsize = read(fd, &chunk_file_hdr, CHUNK_FILE_HEADER_SZ);<br />if (rwsize != CHUNK_FILE_HEADER_SZ) {<br />ret = -1;<br />goto _CHUNK_FILE_PROCESS_EXIT;<br />}<br />if (sim_algo == LCS_YES) {<br />le->str = alloc_2d_array(chunk_file_hdr.block_nr, MD5_LEN);<br />if (le->str == NULL) {<br />ret = -1;<br />goto _CHUNK_FILE_PROCESS_EXIT;<br />}<br />le->len = chunk_file_hdr.block_nr;<br />}<br />for(i = 0; i < chunk_file_hdr.block_nr; i++) {<br />rwsize = read(fd, &chunk_bentry, CHUNK_BLOCK_ENTRY_SZ);<br />if (rwsize != CHUNK_BLOCK_ENTRY_SZ) {<br />ret = -1;<br />goto _CHUNK_FILE_PROCESS_EXIT;<br />}<br />he = (hash_entry *)hash_value((void *)chunk_bentry.md5, htab);<br />if (he == NULL) {<br />he = (hash_entry *)malloc(sizeof(hash_entry));<br />he->nr1 = he->nr2 = 0;<br />he->len = chunk_bentry.len;<br />}<br />(which == FILE1) ? he->nr1++ : he->nr2++;<br />/* insert or update hash entry */<br />hash_insert((void *)strdup(chunk_bentry.md5), (void *)he, htab);<br />if (sim_algo == LCS_YES) {<br />memcpy(le->str[i], chunk_bentry.md5, MD5_LEN);<br />}<br />}<br />_CHUNK_FILE_PROCESS_EXIT:<br />close(fd);<br />return ret;<br />}<br />uint32_t LCS(char** a, int n, char** b, int m, hashtable *htab)<br />{<br /> int** S;<br /> int** R;<br /> int ii;<br /> int jj;<br /> int pos;<br /> uint32_t len = 0;<br />hash_entry *he = NULL;<br />/* Memory allocation */<br /> S = (int **)malloc( (n+1) * sizeof(int *) );<br /> R = (int **)malloc( (n+1) * sizeof(int *) );<br />if (S == NULL || R == NULL) {<br />perror("malloc for S and R in LCS");<br />exit(0);<br />}<br /> for(ii = 0; ii <= n; ++ii) {<br /> S[ii] = (int*) malloc( (m+1) * sizeof(int) );<br /> R[ii] = (int*) malloc( (m+1) * sizeof(int) );<br />if (S[ii] == NULL || R[ii] == NULL) {<br />perror("malloc for S[ii] and R[ii] in LCS");<br />exit(0);<br />}<br /> }<br /> /* It is important to use <=, not <. The next two for-loops are initialization */<br /> for(ii = 0; ii <= n; ++ii) {<br /> S[ii][0] = 0;<br /> R[ii][0] = UP;<br /> }<br /> for(jj = 0; jj <= m; ++jj) {<br /> S[0][jj] = 0;<br /> R[0][jj] = LEFT;<br /> }<br /> /* This is the main dynamic programming loop that computes the score and */<br /> /* backtracking arrays. */<br /> for(ii = 1; ii <= n; ++ii) {<br /> for(jj = 1; jj <= m; ++jj) {<br /> if (strcmp(a[ii-1], b[jj-1]) == 0) {<br /> S[ii][jj] = S[ii-1][jj-1] + 1;<br /> R[ii][jj] = UP_AND_LEFT;<br /> }<br /> else {<br /> S[ii][jj] = S[ii-1][jj-1] + 0;<br /> R[ii][jj] = NEITHER;<br /> }<br /> if( S[ii-1][jj] >= S[ii][jj] ) {<br /> S[ii][jj] = S[ii-1][jj];<br /> R[ii][jj] = UP;<br /> }<br /> if( S[ii][jj-1] >= S[ii][jj] ) {<br /> S[ii][jj] = S[ii][jj-1];<br /> R[ii][jj] = LEFT;<br /> }<br /> }<br /> }<br /> /* The length of the longest substring is S[n][m] */<br /> ii = n;<br /> jj = m;<br /> pos = S[ii][jj];<br /> /* Trace the backtracking matrix. */<br /> while( ii > 0 || jj > 0 ) {<br /> if( R[ii][jj] == UP_AND_LEFT ) {<br /> ii--;<br /> jj--;<br /> //lcs[pos--] = a[ii];<br />he = (hash_entry *)hash_value((void *)a[ii], htab);<br />len += ((he == NULL) ? 0: he->len);<br /> }<br /> else if( R[ii][jj] == UP ) {<br /> ii--;<br /> }<br /> else if( R[ii][jj] == LEFT ) {<br /> jj--;<br /> }<br /> }<br /> for(ii = 0; ii <= n; ++ii ) {<br /> free(S[ii]);<br /> free(R[ii]);<br /> }<br /> free(S);<br /> free(R);<br />return len;<br />}<br />int hash_callback(void *key, void *data)<br />{<br />hash_entry *he = (hash_entry *)data;<br />sim_union += (he->len * (he->nr1 + he->nr2));<br />sim_intersect += (he->len * MIN(he->nr1, he->nr2));<br />}<br />static float similarity_detect(hashtable *htab, char **str1, int n, char **str2, int m, int sim_algo)<br />{<br />uint32_t lcs_len = 0;<br />hash_for_each_do(htab, hash_callback);<br />if (sim_algo == LCS_YES) {<br />lcs_len = LCS(str1, n, str2, m, htab);<br />return lcs_len * 2.0 / sim_union;<br />} else { /* LCS_NOT */<br />return sim_intersect * 2.0 / sim_union;<br />}<br />}<br />int main(int argc, char *argv[])<br />{<br />int chunk_algo = CHUNK_CDC;<br />int sim_algo = LCS_NOT;<br />char *file1 = NULL;<br />char *file2 = NULL;<br />lcs_entry le1, le2;<br />char tmpname[NAME_MAX_SZ] = {0};<br />char template[] = "deduputil_bsim_XXXXXX";<br />hashtable *htab = NULL;<br />int ret = 0;<br />if (argc < 5) {<br />usage();<br />return -1;<br />}<br />/* parse chunk algorithms */<br />file1 = argv[1];<br />file2 = argv[2];<br />chunk_algo = parse_arg(argv[3]);<br />sim_algo = parse_arg(argv[4]);<br />if (chunk_algo == -1 || sim_algo == -1) {<br />usage();<br />return -1;<br />}<br />htab = create_hashtable(HASHTABLE_BUCKET_SZ);<br />if (htab == NULL) {<br />fprintf(stderr, "create hashtabke failed/n");<br />return -1;<br />}<br />/* chunk file1 and file2 into blocks */<br />sprintf(tmpname, "/tmp/%s_%d", mktemp(template), getpid());<br />ret = file_chunk(file1, tmpname, chunk_algo);<br />if (0 != ret) {<br />fprintf(stderr, "chunk %s failed/n", file1);<br />goto _BENCODE_EXIT;<br />}<br />le1.str = NULL;<br />ret = chunk_file_process(tmpname, htab, FILE1, sim_algo, &le1);<br />if (ret != 0) {<br />fprintf(stderr, "pasre %s failed/n", file1);<br />goto _BENCODE_EXIT;<br />}<br />ret = file_chunk(file2, tmpname, chunk_algo);<br />if (0 != ret){<br />fprintf(stderr, "chunk %s failed/n", file2);<br />goto _BENCODE_EXIT;<br />}<br />le2.str = NULL;<br />ret = chunk_file_process(tmpname, htab, FILE2, sim_algo, &le2);<br />if (ret != 0) {<br />fprintf(stderr, "pasre %s failed/n", file2);<br />goto _BENCODE_EXIT;<br />}<br />fprintf(stderr, "similarity = %.4f/n", similarity_detect(htab, le1.str, le1.len, le2.str, le2.len, sim_algo));<br />_BENCODE_EXIT:<br />unlink(tmpname);<br />hash_free(htab);<br />if (le1.str) free_2d_array(le1.str);<br />if (le2.str) free_2d_array(le2.str);<br />return ret;<br />}<br />

 

 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.