哈希沖突比你想象的多


哈希函數是映射函數,它把輸入的數據值經過一定的轉換算法,映射成為新的數據值,哈希算法質量的好壞,是由產生的數據值的精確度決定的,理想的哈希函數有兩個特性:對於同一個輸入值,產生相同的哈希值;對於不同的輸入值,產生不同的哈希值。對於不同的輸入值,產生相同的哈希值,這就叫沖突,沖突越少,哈希算法的質量越高。SQL Server內置三個哈希函數,2個校驗和函數(checksum 和 binary_checksum),以及一個沖突更少的哈希函數HashBytes,這三個函數都無法提供100%的精確度,如果業務邏輯要求不允許有誤差,那么不要使用任何哈希函數,只要是哈希函數,就會存在沖突。

一,校驗和

在一些數據表中,如果存在多個字符串字段,並且字符串非常長,在比較字符串是否相同時,使用校驗和函數,將其轉換成 int 類型,能夠提高數據比較的速度。但是不要忽略官方文檔的警告(Caution):使用校驗和函數,有時不能探測到數據的更新,除非應用程序能夠容忍偶爾的丟失更新。一旦將校驗和函數用於實際的項目中,你會發現,不是偶爾,而是經常會發生探測不到數據更新的異常:

BINARY_CHECKSUM(*) will return a different value for most, but not all, changes to the row, and can be used to detect most row modifications.

However, there is a small chance that the checksum will not change. For this reason, we do not recommend using CHECKSUM to detect whether values have changed, unless your application can tolerate occasionally missing a change.

由於存在僥幸心理,在項目中使用了校驗函數對字符串字段進行比較,代碼大致如下:url,why 和 description 字段都是挺長的字符串字段,特別是description,平均長度是1163字符。

;merge target as t 
using source as s 
    on t.id=s.id
when matched 
    and binary_checksum(t.url,t.why,t.description)
            <>binary_checksum(s.url,s.why,s.description)
....

大多數情況下,函數工作正常,但是,常在河邊走,哪能不濕鞋,在一個周末,領導一個短信,我回來加班了,校驗函數不能探測出更新的數據,導致數據同步出現問題,對ScoreCard和績效影響較大,必須及時修改。

如圖,Source 和 target 的Description 字段不一致,但是校驗函數:binary_checksum(url,description,why) 返回的值是相同的,痛定思痛,為了避免以后的數據更新被異常丟失,決定在ETL中不再使用校驗和函數,而是直接使用字符串進行比對。

校驗和函數對長字符串產生的校驗值質量不是很高,那對短的字符串了?經過多次嘗試,發現校驗函數在短的字符串上更容易出現更新丟失的情況,鑒於校驗函數的這些缺點,強烈建議不要在項目中使用校驗和函數。直接使用字符串進行比較,保證萬無一失,相比數據更新出錯,數據同步稍稍慢點,更能被容忍。

在比較長的字符串時,直接比較會比較慢,使用校驗和比較速度會比較高,但是 binary_checksum 和 checksum 產生的校驗和的質量比較低,建議不要再項目中使用校驗函數。當字符串比較短時,很大可能產生Hash 沖突,例如:下面的腳本返回兩行數據,每行的兩個數據列結果相同,相比較而言,使用 checksum 比 binary_checksum效果更好。

--database collation is SQL_Latin1_General_CP1_CI_AS select checksum(N'us',N'FL',N'Land O Lakes'),checksum(N'us',N'FL',N'Land O'' Lakes') select binary_checksum(N'gb',N'c6',N'bude'),binary_checksum(N'no',N'',N'myre')

二,HashBytes函數

HashBytes 函數使用不同級別的加密算法,能夠產生高質量的哈希值,大幅度提高識別數據差異的准確性,但是HashBytes函數無法提供100%的准確度,如果業務邏輯要求不允許有誤差,那么不要使用任何Hash 函數,只要是Hash函數,就會存在沖突。HashBytes 函數對於相同的文本,有時會產生不同的哈希值。

When an MD5 hash algorithm is specified, the probability of HashBytes returning the same result for two different inputs is much lower than that of CHECKSUM.

HashBytes 只能有一個輸入參數,參數的數據類型是varchar、nvarchar 或 varbinary,並且輸入字符的最大有效字節數量是8000Bytes,產生的哈希值跟輸入值的數據類型相關。

1,指定Hash算法

使用 HashBytes需要指定算法,SQL Server內置7種計算HashValue的算法,推薦使用MD5來計算數據的差異。

  HASHBYTES ( '<algorithm>', { @input | 'input' } ) 
  <algorithm>::= MD2 | MD4 | MD5 | SHA | SHA1 | SHA2_256 | SHA2_512

輸入參數的數據類型為 varchar、nvarchar 或 varbinary,輸入參數的有效字節數量最大是8000bytes。HashBytes函數的返回值的數據類型是 varbinary ,不同的算法,返回的字節長度是不同的

The output conforms to the algorithm standard: 128 bits (16 bytes) for MD2, MD4, and MD5; 160 bits (20 bytes) for SHA and SHA1; 256 bits (32 bytes) for SHA2_256, and 512 bits (64 bytes) for SHA2_512.

三,影響HashBytes函數的因素

1,HashBytes函數受輸入數據類型的影響

select HASHBYTES('SHA1',N'123abc') as HashNChar, HASHBYTES('SHA1','123abc') as HashChar;

2,HashBytes函數受輸入字符大小寫的影響

select hashbytes('SHA1','eric') as UpperCase,hashbytes('SHA1','Eric') as LowerCase

3,有效輸入是8000Bytes

如果輸入字符串的字節數量大於8000Bytes,那么HashBytes 把超過8000Bytes的字符舍棄,只對前8000Bytes進行Hash計算。SQL Server 不會提供任何提示或warning,在使用時,必須保證,輸入字符串的字節數量不能超過8000。

如果是Unicode字符,那么輸入字符串字符數量不能超過4000個;如果是varchar字符,那么輸入字符串的數量不能超過8000個。

select hashbytes('SHA1',replicate('eric',2000)) ,hashbytes('SHA1',replicate('eric',4000))


參考文檔:

HASHBYTES (Transact-SQL)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM