雜湊演算法的應用

2021-10-06 22:29:52 字數 3784 閱讀 2784

我們前面幾節講到「雜湊表」「雜湊函式」,這裡又講到「雜湊演算法」,你是不是有點一頭霧水?實際上,不管是「雜湊」還是「雜湊」,這都是中文翻譯的差別,英文其實就是「hash」。所以,我們常聽到有人把「雜湊表」叫作「雜湊表」「hash 表」,把「雜湊演算法」叫作「hash 演算法」或者「雜湊演算法」。那到底什麼是雜湊演算法呢?

雜湊演算法的定義和原理非常簡單,基本上一句話就可以概括了。將任意長度的二進位制值串對映為固定長度的二進位制值串,這個對映的規則就是雜湊演算法,而通過原始資料對映之後得到的二進位制值串就是雜湊值。但是,要想設計乙個優秀的雜湊演算法並不容易,根據我的經驗,我總結了需要滿足的幾點要求:

1.從雜湊值不能反向推導出原始資料(所以雜湊演算法也叫單向雜湊演算法);

2.對輸入資料非常敏感,哪怕原始資料只修改了乙個 bit,最後得到的雜湊值也大不相同;

3.雜湊衝突的概率要很小,對於不同的原始資料,雜湊值相同的概率非常小;

4.雜湊演算法的執行效率要盡量高效,針對較長的文字,也能快速地計算出雜湊值。

這些定義和要求都比較理論,可能還是不好理解,我拿 md5 這種雜湊演算法來具體說明一下。

我們分別對「今天我來講雜湊演算法」和「jiajia」這兩個文字,計算 md5 雜湊值,得到兩串看起來毫無規律的字串(md5 的雜湊值是 128 位的 bit 長度,為了方便表示,我把它們轉化成了 16 進製編碼)。可以看出來,無論要雜湊的文字有多長、多短,通過 md5 雜湊之後,得到的雜湊值的長度都是相同的,而且得到的雜湊值看起來像一堆隨機數,完全沒有規律。

md5

("今天我來講雜湊演算法"

)= bb4767201ad42c74e650c1b6c03d78fa

md5(

"jiajia"

)= cd611a31ea969b908932d44d126d195b

我們再來看兩個非常相似的文字,「我今天講雜湊演算法!」和「我今天講雜湊演算法」。這兩個文字只有乙個感嘆號的區別。如果用 md5 雜湊演算法分別計算它們的雜湊值,你會發現,儘管只有一字之差,得到的雜湊值也是完全不同的。

md5

("我今天講雜湊演算法!")=

425f

0d5a917188d2c3c3dc85b5e4f2cb

md5(

"我今天講雜湊演算法"

)= a1fb91ac128e6aa37fe42c663971ac3d

我在前面也說了,通過雜湊演算法得到的雜湊值,很難反向推導出原始資料。比如上面的例子中,我們就很難通過雜湊值「a1fb91ac128e6aa37fe42c663971ac3d」反推出對應的文字「我今天講雜湊演算法」。雜湊演算法要處理的文字可能是各種各樣的。比如,對於非常長的文字,如果雜湊演算法的計算時間很長,那就只能停留在理論研究的層面,很難應用到實際的軟體開發中。比如,我們把今天這篇包含 4000 多個漢字的文章,用 md5 計算雜湊值,用不了 1ms 的時間。

雜湊演算法的應用非常非常多,我選了最常見的七個,分別是安全加密、唯一標識、資料校驗、雜湊函式、負載均衡、資料分片、分布式儲存。這節我們先來看前四個應用。

說到雜湊演算法的應用,最先想到的應該就是安全加密。

最常用於安全加密的雜湊演算法是md5(訊息摘要演算法)和sha(安全雜湊演算法)。

前面我講到的雜湊演算法四點要求,對用於加密的雜湊演算法來說,有兩點格外重要。第一點是很難根據雜湊值反向推導出原始資料,第二點是雜湊衝突的概率要很小。

我先來舉乙個例子。如果要在海量的相簿中,搜尋一張圖是否存在,我們不能單純地用的元資訊(比如名稱)來比對,因為有可能存在名稱相同但內容不同,或者名稱不同內容相同的情況。那我們該如何搜尋呢?

我們知道,任何檔案在計算中都可以表示成二進位製碼串,所以,比較笨的辦法就是,拿要查詢的的二進位製碼串與相簿中所有的二進位製碼串一一比對。如果相同,則說明在相簿中存在。但是,每個小則幾十 kb、大則幾 mb,轉化成二進位制是乙個非常長的串,比對起來非常耗時。有沒有比較快的方法呢?

我們可以給每乙個取乙個唯一標識,或者說資訊摘要。比如,我們可以從的二進位製碼串開頭取 100 個位元組,從中間取 100 個位元組,從最後再取 100 個位元組,然後將這 300 個位元組放到一塊,通過雜湊演算法(比如 md5),得到乙個雜湊字串,用它作為的唯一標識。通過這個唯一標識來判定是否在相簿中,這樣就可以減少很多任務作量。

簡而言之,雜湊演算法可以對大資料做資訊摘要,通過乙個較短的二進位制編碼來表示很大的資料。

前面講了很多雜湊演算法的應用,實際上,雜湊函式也是雜湊演算法的一種應用。

我們前兩節講到,雜湊函式是設計乙個雜湊表的關鍵。它直接決定了雜湊衝突的概率和雜湊表的效能。不過,相對雜湊演算法的其他應用,雜湊函式對於雜湊演算法衝突的要求要低很多。即便出現個別雜湊衝突,只要不是過於嚴重,我們都可以通過開放定址法或者鍊錶法解決。不僅如此,雜湊函式對於雜湊演算法計算得到的值,是否能反向解密也並不關心。雜湊函式中用到的雜湊演算法,更加關注雜湊後的值是否能平均分布,也就是,一組資料是否能均勻地雜湊在各個槽中。除此之外,雜湊函式執行的快慢,也會影響雜湊表的效能,所以,雜湊函式用的雜湊演算法一般都比較簡單,比較追求效率。

如何才能實現乙個負載均衡演算法呢?也就是說,我們需要在同乙個客戶端上,在一次會話中的所有請求都路由到同乙個伺服器上

最直接的方法就是,維護一張對映關係表,這張表的內容是客戶端 ip 位址或者會話 id 與伺服器編號的對映關係。客戶端發出的每次請求,都要先在對映表中查詢應該路由到的伺服器編號,然後再請求編號對應的伺服器。這種方法簡單直觀,但也有幾個弊端:

如果客戶端很多,對映表可能會很大,比較浪費記憶體空間;

客戶端下線、上線,伺服器擴容、縮容都會導致對映失效,這樣維護對映表的成本就會很大;

如果借助雜湊演算法,這些問題都可以非常完美地解決。我們可以通過雜湊演算法,對客戶端 ip 位址或者會話 id 計算雜湊值,將取得的雜湊值與伺服器列表的大小進行取模運算,最終得到的值就是應該被路由到的伺服器編號。 這樣,我們就可以把同乙個 ip 過來的所有請求,都路由到同乙個後端伺服器上。(其本質就是因為雜湊演算法可以將任意長度的二進位制值串對映為固定長度的二進位制值串,也就是說將ip等無法取進行取餘計算的資料形式轉化為可以取餘計算的資料形式。這本質上是乙個資料轉換的應用問題)

現在網際網路面對的都是海量的資料、海量的使用者。我們為了提高資料的讀取、寫入能力,一般都採用分布式的方式來儲存資料,比如分布式快取。我們有海量的資料需要快取,所以乙個快取機器肯定是不夠的。於是,我們就需要將資料分布在多台機器上。

該如何決定將哪個資料放到哪個機器上呢?我們可以借用前面資料分片的思想,即通過雜湊演算法對資料取雜湊值,然後對機器個數取模,這個最終值就是應該儲存的快取機器編號。

但是,如果資料增多,原來的 10 個機器已經無法承受了,我們就需要擴容了,比如擴到 11 個機器,這時候麻煩就來了。因為,這裡並不是簡單地加個機器就可以了。原來的資料是通過與 10 來取模的。比如 13 這個資料,儲存在編號為 3 這台機器上。但是新加了一台機器中,我們對資料按照 11 取模,原來 13 這個資料就被分配到 2 號這台機器上了。

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

所以,我們需要一種方法,使得在新加入乙個機器後,並不需要做大量的資料搬移。這時候,一致性雜湊演算法就要登場了。

假設我們有 k 個機器,資料的雜湊值的範圍是[0, max]。我們將整個範圍劃分成 m 個小區間(m 遠大於 k),每個機器負責 m/k 個小區間。當有新機器加入的時候,我們就將某幾個小區間的資料,從原來的機器中搬移到新的機器中。這樣,既不用全部重新雜湊、搬移資料,也保持了各個機器上資料數量的均衡。

關於一致性雜湊演算法的詳細介紹,參看這篇部落格五分鐘徹底理解一致性雜湊演算法

除了我們上面講到的分布式快取,實際上,一致性雜湊演算法的應用非常廣泛,在很多分布式儲存系統中,都可以見到一致性雜湊演算法的影子。

雜湊演算法的應用

將任意長度的二進位制值串對映成固定長度的二進位制值串,這個對映規則就是雜湊演算法。通過原始資料對映之後得到的二進位制值串就是雜湊值。乙個優秀的雜湊演算法需滿足以下幾點 從雜湊值不能反向推導出原始資料 所以雜湊演算法也叫單向雜湊演算法 對輸入資料非常敏感,即使原始資料只修改了乙個bit,最後得到的雜湊...

雜湊演算法的應用

不管是 雜湊 還是 雜湊 這都是中文翻譯的差別,英文其實就是 hash 1.1 定義 將任意長度的二進位制值串對映成固定長度的二進位制值串,這個對映的規則就是雜湊演算法,而通過原始資料對映之後得到的二進位制值串就是雜湊值。1.2 如何設計乙個優秀的雜湊演算法?單向雜湊 從雜湊值不能反向推導出雜湊值 ...

雜湊演算法的理解和應用

我們下面引用一段文本來解釋什麼是雜湊演算法 雜湊是一種加密演算法 雜湊函式 hash function 也稱為雜湊函式或雜湊函式。雜湊函式是乙個公開函式,可以將任意長度的訊息m對映成為乙個長度較短且長度固定的值h m 稱h m 為雜湊值 雜湊值 hash value 雜湊值或者訊息摘要 messag...