1. 程式人生 > >資料結構與演算法之美專欄學習筆記-雜湊演算法

資料結構與演算法之美專欄學習筆記-雜湊演算法

雜湊演算法的定義和原理

將任意長度的二進位制串對映為固定長度的二進位制串。

這個對映的規則就是雜湊演算法,而通過原始資料對映之後得到的二進位制串就是雜湊值。

設計一個優秀的雜湊演算法需要滿足:

  • 從雜湊值不能反向推匯出原始資料(所以雜湊演算法也叫單向雜湊演算法);
  • 對輸入資料非常敏感,哪怕原始資料只修改了一個 Bit,最後得到的雜湊值也大不相同;
  • 雜湊衝突的概率要很小,對於不同的原始資料,雜湊值相同的概率非常小;
  • 雜湊演算法的執行效率要儘量高效,針對較長的文字,也能快速地計算出雜湊值。

 

MD5雜湊演算法

MD5的雜湊值是128位的bit長度,為了方便轉換成了16進位制編碼。

可以看出無論雜湊值文字有多長多短,通過MD5雜湊之後,得到的雜湊值的長度都是一樣的,

而且得到的雜湊值看起來像一堆隨機數完全沒有規律。

MD5(" 今天我來講雜湊演算法 ") = bb4767201ad42c74e650c1b6c03d78fa
MD5("jiajia") = cd611a31ea969b908932d44d126d195b

 

雜湊演算法的應用

安全加密

加密演算法

最常用於加密的雜湊演算法是MD5(MD5 Message-Digest Algorithm,MD5 訊息摘要演算法)和SHA(Secure Hash Algorithm,安全雜湊演算法)。

對用於加密的雜湊演算法來說,有兩點格外重要:

  • 很難根據雜湊值反向推匯出原始資料,
  • 雜湊衝突的概率要很小。

實際上,不管是什麼雜湊演算法,我們只能儘量減

少碰撞衝突的概率,理論上是沒辦法做到完全不衝突的。

鴿巢理論

這裡就基於組合數學中一個非常基礎的理論,鴿巢原理(也叫抽屜原理)。

這個原理本身很簡單,它是說如果有 10 個鴿巢,有 11 只鴿子那肯定有 1 個鴿巢中的鴿子數量多於 1 個。

換句話說就是,肯定有 2 只鴿子在 1 個鴿巢內。

我們知道,雜湊演算法產生的雜湊值的長度是固定且有限的。

雜湊值是固定的 128 位二進位制串,能表示的資料是有限的最多能表示 2^128 個數據。

基於鴿巢原理,如果我們對 2^128+1 個數據求雜湊值,就必然會存在雜湊值相同的情況。

 

唯一標識

在相簿中搜索圖片

如果要在海量的相簿中,搜尋一張圖是否存在,我們不能單純地用圖片的元資訊(比如圖片名稱)來比對。

因為有可能存在名稱相同但圖片內容不同,或者名稱不同圖片內容相同的情況。

我們可以給每一個圖片取一個唯一標識,或者說資訊摘要。

比如,我們可以從圖片的二進位制碼串開頭取 100 個位元組,從中間取 100 個位元組,從最後再取 100 個位元組。

然後將這 300 個位元組放到一塊,通過雜湊演算法(比如 MD5)得到一個雜湊字串,用它作為圖片的唯一標識。

通過這個唯一標識來判定圖片是否在相簿中,這樣就可以減少很多工作量

效能提升

如果還想繼續提高效率,我們可以把每個圖片的唯一標識和相應的圖片檔案在相簿中的路徑資訊,都儲存在散列表中。

當要檢視某個圖片是不是在相簿中的時候,我們先通過雜湊演算法對這個圖片取唯一標識,然後在散列表中查詢是否存在這個唯一標識。

 

資料校驗

P2P檔案快校驗

BT 下載的原理是基於 P2P 協議的。

我們從多個機器上並行下載一個 2GB 的電影,這個電影檔案可能會被分割成很多檔案塊(比如可以分成100 塊,每塊大約 20MB)。

等所有的檔案塊都下載完成之後,再組裝成一個完整的電影檔案就行了。

網路傳輸是不安全的,下載的檔案塊有可能是被宿主機器惡意修改過的,又或者下載過程中出現了錯誤,所以下載的檔案塊可能不是完整的。

解決方法

我們通過雜湊演算法,對 100 個檔案塊分別取雜湊值並且儲存在種子檔案中。

當檔案塊下載完成之後,我們可以通過相同的雜湊演算法,對下載好的檔案塊逐一求雜湊值,然後跟種子檔案中儲存的雜湊值比對。

如果不同,說明這個檔案塊不完整或者被篡改了,需要再重新從其他宿主機器上下載這個檔案塊。

 

雜湊函式

雜湊函式是一種雜湊演算法

實際上,雜湊函式也是雜湊演算法的一種應用。

雜湊函式是設計一個散列表的關鍵。

它直接決定了雜湊衝突的概率和散列表的效能。

不過,相對雜湊演算法的其他應用,雜湊函式對於雜湊演算法衝突的要求要低很多。

即便出現個別雜湊衝突,只要不是過於嚴重,我們都可以通過開放定址法或者連結串列法解決。

雜湊函式追求平均分佈

不僅如此,雜湊函式對於雜湊演算法計算得到的值,是否能反向解密也並不關心。

雜湊函式中用到的雜湊演算法,更加關注雜湊後的值是否能平均分佈。

也就是一組資料是否能均勻地雜湊在各個槽中。

除此之外,雜湊函式執行的快慢,也會影響散列表的效能,

所以,雜湊函式用的雜湊演算法一般都比較簡單,比較追求效率。

 

負載均衡

我們需要在同一個客戶端上,在一次會話中的所有請求都路由到同一個伺服器上。

最直接的方法就是,維護一張對映關係表,這張表的內容是客戶端 IP 地址或者會話 ID 與伺服器編號的對映關係。

客戶端發出的每次請求,都要先在對映表中查詢應該路由到的伺服器編號,然後再請求編號對應的伺服器。

這種方法簡單直觀,但也有幾個弊端:

  • 如果客戶端很多,對映表可能會很大,比較浪費記憶體空間;
  • 客戶端下線、上線,伺服器擴容、縮容都會導致對映失效,這樣維護對映表的成本就會很大;

如果藉助雜湊演算法,這些問題都可以非常完美地解決。

我們可以通過雜湊演算法,對客戶端 IP 地址或者會話 ID 計算雜湊值,將取得的雜湊值與伺服器列表的大小進行取模運算,最終得到的值就是應該被路由到的伺服器編號。

這樣,我們就可以把同一個 IP 過來的所有請求,都路由到同一個後端伺服器上。

 

資料分片

統計“搜尋關鍵詞”出現的次數

假如我們有 1T 的日誌檔案,這裡面記錄了使用者的搜尋關鍵詞,

我們想要快速統計出每個關鍵詞被搜尋的次數。

我們可以先對資料進行分片,然後採用多臺機器處理的方法,來提高處理速度。

為了提高處理的速度,我們用 n 臺機器並行處理。

我們從搜尋記錄的日誌檔案中,依次讀出每個搜尋關鍵詞,

並且通過雜湊函式計算雜湊值,然後再跟 n 取模,最終得到的值,就是應該被分配到的機器編號。

這樣,雜湊值相同的搜尋關鍵詞就被分配到了同一個機器上。

也就是說,同一個搜尋關鍵詞會被分配到同一個機器上。

每個機器會分別計算關鍵詞出現的次數,最後合併起來就是最終的結果。

快速判斷圖片是否在相簿中

假設現在我們的相簿中有 1 億張圖片,在單臺機器上構建散列表是行不通的。

我們同樣可以對資料進行分片,然後採用多機處理。

我們準備 n 臺機器,讓每臺機器只維護某一部分圖片對應的散列表。

我們每次從相簿中讀取一個圖片,計算唯一標識,

然後與機器個數 n 求餘取模,得到的值就對應要分配的機器編號,

然後將這個圖片的唯一標識和圖片路徑發往對應的機器構建散列表。

當我們要判斷一個圖片是否在相簿中的時候,我們通過同樣的雜湊演算法,

計算這個圖片的唯一標識,然後與機器個數 n 求餘取模。

假設得到的值是 k,那就去編號 k 的機器構建的散列表中查詢。

 

分散式儲存

我們有海量的資料需要快取,所以一個快取機器肯定是不夠的。

我們需要將資料分佈在多臺機器上通過雜湊演算法對資料取雜湊值,然後對機器個數取模,這個最終值就是應該儲存的快取機器編號

如果資料增多,原來的 10 個機器已經無法承受,我們需要擴容,假如擴到 11 個機器。

原來的資料是通過與 10 來取模的,比如 13 這個資料,儲存在編號為 3 這臺機器上。

新加了一臺機器後,我們對資料按照 11 取模,原來 13 這個資料被分配到了 2 號這臺機器上。

因此,所有的資料都要重新計算雜湊值,然後重新搬移到正確的機器上。

所有的資料請求都會穿透快取,直接去請求資料庫,可能會發生雪崩效應,壓垮資料庫。

我們需要一種方法,使得在新加入一個機器後,並不需要做大量的資料搬移。

這時候,一致性雜湊演算法就要登場了。

一致性雜湊演算法

假設我們有 k 個機器,資料的雜湊值的範圍是 [0, MAX]。

我們將整個範圍劃分成 m 個小區間(m 遠大於 k),每個機器負責 m/k 個小區間。

當有新機器加入的時候,我們就將某幾個小區間的資料,從原來的機器中搬移到新的機器中。

這樣既不用全部重新雜湊、搬移資料,也保持了各個機器上資料數量的均衡。