開發面試Hash常見算法


Hash部分分爲三部分講解,各位遊客可根據分類進行對應博客閱讀:

  1. 開發面試Hash原理詳解
  2. 開發面試Hash常見算法
  3. 開發面試Hash面試考題

博客書寫不易,您的點贊收藏是我前進的動力,千萬別忘記點贊、 收藏 ^ _ ^ !

本部分主要講解幾種在實際開發中常被運用的幾種Hash算法。

1. 一致性Hash算法

一致性Hash算法在1997年由麻省理工學院提出的一種分佈式哈希(DHT)實現算法,設計目標是爲了解決因特網中的熱點(Hot Spot)問題,初衷和CARP十分相似。一致性Hash修正了CARP使用的簡單哈希算法帶來的問題,使得分佈式哈希(DHT)可以在P2P環境中真正得到應用。

Consistent Hashing 一致性hash的原理簡單的來說,就是在移除 / 添加一個 cache 時,它能夠儘可能小的改變已存在key 映射關係,儘可能的滿足單調性的要求。

使用場景

比如你有 N 個 cache 服務器(後面簡稱 cache ),那麼如何將一個對象 object 映射到 N 個 cache 上呢,你很可能會採用類似下面的通用H(key)=Key%N的方法計算 object 的 hash 值,然後均勻的映射到到 N 個 cache 。

使用簡單取餘hash算法不能解決分佈式問題,如果大量用戶數據在存儲分佈式中使用Hash(Key)=object%N,N指N個cache服務器或者節點,用這種hash算法是不滿足分佈式要求的。我們如下分析:

  1. 如果N個cache服務器中編號爲a的服務器故障了,需要把a從服務器羣中移除,這個時候cache服務器的數量就變成了N-1臺,那麼所有對象(object)映射到cache服務器的計算公式就變成了hash(object)%N-1,對,影響到了所有的對象與cache服務器的映射關係。

  2. 類似,由於訪問加重,需要添加cache服務器,這時候cache服務器是N+1臺,映射公式就變成了hash(object)%N+1,這就意味着幾乎所有的cache都失效了。

  3. 由於硬件能力越來越強,你可能想讓後面添加的節點多做點活,但是用Hash(Key)=object%N算法無法做到有效分配任務

如1、2兩種情況意味着突然之間幾乎所有的 cache 都失效了。對於服務器而言這是一場災難,洪水般的訪問都會直接衝向後臺服務器。

在分佈式集羣中,對機器的添加刪除,或者機器故障後自動脫落集羣這些操作是分佈式集羣管理最基本的功能。如果採用常用的hash(object)%N算法,那麼在有機器添加或者刪除後,很多原有的數據就無法找到了,這樣嚴重的違反了單調性原則,所以就出現了一致性hash算法來解決分佈式中碰到的問題。

一致性hash算法要求

在動態變化的Cache環境中,良好的一致性hash算法應該滿足以下幾個方面
1.平衡性(Balance)
平衡性是指哈希的結果能夠儘可能分佈在所有的緩衝(Cache)中去,這樣可以使得所有的緩衝空間得到利用。很多哈希算法都能夠滿足這一條件。

2.單調性(Monotonicity)
單調性是指如果已經有一些內容通過哈希分派到了相應的緩衝中,又有新的緩衝加入到系統中。哈希的結果應該能夠保證原有已分配的內容可以被映射到原有的或者新的緩衝中去,而不會映射到舊的緩衝集合中的其他緩衝區。

簡單的哈希算法往往不能滿足單調性的要求,如最簡單的線性哈希:x = (ax + b) mod §,在上式中,P表示全部緩衝的大小。不難看出,當緩衝大小發生變化時(從P1到P2),原來所有的哈希結果均會發生變化,從而不滿足單調性的要求。哈希結果的變化意味着當緩衝空間發生變化時,所有的映射關係需要在系統內全部更新。

而在P2P系統內,緩衝的變化等價於Peer加入或退出系統,這一情況在P2P系統中會頻繁發生,因此會帶來極大計算和傳輸負荷。單調性就是要求哈希算法能夠應對這種情況。

3.分散性(Spread)
在分佈式環境中,終端有可能看不到所有的緩衝,而只能看到其中的一部分。當終端希望通過哈希過程將內容映射到緩衝上去,由於不同終端所見的緩衝範圍有可能不同,從而導致哈希的結果不一致,最終的結果是相同的內容被不同的終端映射到不同的緩衝區中。這種情況顯然是應該避免的,因爲它導致相同內容被存儲到不同緩衝中去,降低了系統存儲的效率。

分散性的定義就是上述情況發生的嚴重程度。好的哈希算法應該能夠儘量避免不一致的情況發生,也就是儘量降低分散性。

4.負載(Load)
負載問題實際上是從另一個角度看待分散性問題。既然不同的終端可能將相同的內容映射到不同的緩衝區中,那麼對於一個特定的緩衝區而言,也可能被不同的用戶映射到不同的內容。與分散性一樣,這種情況也是應當避免的,因此好的哈希算法應能夠儘量降低緩衝的負荷。

5.平滑性(Smoothness)
平滑性是指緩存服務器的數目平滑改變和緩存對象的平滑改變是一致的。

Hash環形空間

按照常用的hash算法來將對應的key哈希到一個具有232次方個桶的空間中,即0~(232)-1的數字空間。現在我們可以將這些數字頭尾相連,想象成一個閉合的環形。整個空間按順時針方向組織,0和2^32-1在零點中方向重合。如下圖:
在這裏插入圖片描述

1.現在我們把數據(對象)通過一定的hash算法處理後映射到環上
假設有object1、object2、object3、object4四個對象通過特定的Hash函數計算出對應的key值,然後散列到hash換上。如下圖:
Hash(object1)=key1;
Hash(object2)=key2;
Hash(object3)=key3;
Hash(object4)=key4;
在這裏插入圖片描述

2. 將機器通過hash算法映射到環上
在採用一致性哈希算法的分佈式集羣中將新的機器加入,其原理是通過使用與對象存儲一樣的Hash算法將機器也映射到Hash環中。(一般情況下對機器的hash計算是採用機器的IP或者唯一的別名作爲輸入值)。

在這個環形空間中,如果沿着順時針方向從對象的 key 值出發,直到遇見一個 cache ,那麼就將該對象存儲在這個 cache 上,因爲對象和 cache 的 hash 值是固定的,因此這個 cache 必然是唯一和確定的。這樣就將所有對象存儲到了離自己最近的機器中。

假設現在有NODE1,NODE2,NODE3三臺機器中,通過hash算法得到對應的KEY值,映射到環中,其示意圖如下:
Hash(NODE1)=KEY1;
Hash(NODE2)=KEY2;
Hash(NODE3)=KEY3;
在這裏插入圖片描述
通過上圖可以看出對象與機器處於同一個哈希空間中,這樣按順時針轉動object1(對象)存儲到了NODE1(機器)中,object3(對象)存儲到了NODE2(機器)中,object2、object4(對象)存儲到了NODE3(機器)中。

在這樣的部署環境中,hash環是不會變更的,因此,通過算出對象的hash值就能快速的定位到對應的機器中,這樣就能找到對象真正的存儲位置了。

機器刪除與添加

普通hash求餘算法最爲不妥的地方就是在有機器的添加與刪除以後會造成大量的對象存儲位置的失效,這樣就大大的不滿足單調性了。下面來分析一下一致性哈希算法是如何處理的。

1.節點(機器)的刪除
以上面的分佈式集羣爲例,如果NODE2出現故障被刪除了,那麼按照順時針遷移的方法,object3將會被遷移到NODE3中,這樣僅僅是object3的映射位置發生了變化,其他的對象沒有任何的變動,如下圖:
在這裏插入圖片描述

  1. 節點(機器)的添加
    如果往集羣中添加一個新的節點NODE4,通過對應的Hash算法得到KEY4,並映射到環中,如下圖:
    在這裏插入圖片描述
    通過按照順時針遷移的規則,那麼object2被遷移到NODE4中,其他對象還保持這原有的存儲位置。通過對節點的添加和刪除的分析,一致性哈希算法在保持了單調性的同時,還是數據的遷移達到了最小,這樣的算法對分佈式集羣來說非常合適的,避免了大量收數據遷移,減少了服務器的壓力。

平衡性分析

根據上面的圖解分析,一致性哈希算法滿足了單調性和負載均衡的特性以及一般hash算法的分散性,但這還並不能當做其被廣泛應用的原由,因爲缺少了平衡性。

下面將分析一致性哈希算法是如何滿足平衡性的。hash算法是不保證平衡性的,如上面只部署了NODE1和NODE3的情況(NODE2被刪除的圖),object1存儲在NODE1中,而object2、object3、object4都存儲在NODE3中,這樣就造成了非常不平衡的狀態。在一致性哈希算法中,爲了儘可能的滿足平衡性,其引入了虛擬節點。

虛擬節點
虛擬節點(Virtual node)是實際節點(機器)在hash空間的複製品(replica),一個實際節點對應了若干個“虛擬節點”,這個對應個數也稱爲“複製個數”,“虛擬節點”在hash空間中以hash值排列。

在上面只部署了NODE1和NODE3的情況(NODE2被刪除的圖)爲例,之前的對象在機器上的分佈很不均衡,現在我們以2個副本(每個節點複製2個)爲例,這樣整個hash環就存在4個虛擬節點,最後對象映射的關係圖如下:
在這裏插入圖片描述

根據上圖可知對象的映射關係:object1->NODE1-1,object2->NODE1-2 ,object3->NODE3-2,object4->NODE3-1,通過虛擬節點的引入,對象的分佈就比較均衡了。

那麼在實際操作中,真正的對象查詢是如何工作的呢?對象從hash到虛擬節點到實際節點的轉換如下圖:
在這裏插入圖片描述

虛擬節點”的hash計算可以採用對應節點的IP地址加數字後綴的方式。例如假設NODE1的IP地址爲192.168.1.100。引入“虛擬節點”前,計算 cache A 的 hash 值:

Hash(“192.168.1.100”);
引入“虛擬節點”後,計算“虛擬節”點NODE1-1和NODE1-2的hash值:
Hash(“192.168.1.100#1”); // NODE1-1
Hash(“192.168.1.100#2”); // NODE1-2

虛擬節點都對應着實際節點,可以自定義設計怎麼分配。

總結

  1. 一致性哈希算法對於節點的增減都只需重定位環空間中的一小部分數據,具有較好的容錯性和可擴展性。
  2. 一致性哈希算法在保持了單調性的同時,還是數據的遷移達到了最小,這樣的算法對分佈式集羣來說是非常合適的,避免了大量數據遷移,減小了服務器的的壓力。

2. SimHash算法

在這裏插入圖片描述
在比較網頁或信息文本相似度時,理想的hash函數需要對幾乎相同的輸入內容,產生相同或者相近的hash值。換言之,hash值的相似程度要能直接反映輸入內容的相似程度,故md5等傳統hash方法也無法滿足我們的需求。

simhash作爲locality sensitive hash(局部敏感哈希)的一種,是google用於海量文本去重的一種方法。它將一篇文本最後轉換成一個64位的字節,暫且稱之爲特徵字,兩篇文本是否相同比較兩者的海明距離即可做出判斷。

其主要思想是降維,將高維的特徵向量映射成低維的特徵向量,通過兩個向量的Hamming Distance來確定文章是否重複或者高度近似。

局部敏感
假定兩個字符串具有一定的相似性,在hash之後,仍然能保持這種相似性,就稱之爲局部敏感hash。

SimHash算法思路

simhash算法分爲5個步驟:分詞、hash、加權、合併、降維,具體過程如下所述:
1. 分詞
給定一段語句進行分詞,得到有效的特徵向量,然後爲每一個特徵向量設置1-5等5個級別的權重(如果是給定一個文本,那麼特徵向量可以是文本中的詞,其權重可以是這個詞出現的次數)。

例如給定一段語句:“CSDN博客結構之法算法之道的作者July”,分詞後爲:“CSDN&博客&結構&之&法&算法&之&道&的&作者&July”,然後爲每個特徵向量賦予權值:CSDN(4) 博客(5) 結構(3) 之(1) 法(2) 算法(3) 之(1) 道(2) 的(1) 作者(5) July(5)。
其中括號裏的數字代表這個單詞在整條語句中的重要程度,數字越大代表越重要。

2. hash
通過hash函數計算各個特徵向量的hash值,hash值爲二進制數01組成的n-bit簽名。
比如“CSDN”的hash值Hash(CSDN)爲100101,“博客”的hash值Hash(博客)爲“101011”。就這樣,字符串就變成了一系列數字。

這裏計算特徵詞的Hash算法自己選定,但是生成的長度一般一樣長,也有的是直接64位長度,剛好被long型存儲。

3. 加權
在hash值的基礎上,給所有特徵向量進行加權,即W = Hash * weight,且遇到1則hash值和權值正相乘,遇到0則hash值和權值負相乘。

例如給“CSDN”的hash值“100101”加權得到:W(CSDN) = 100101 4 = 4 -4 -4 4 -4 4,給“博客”的hash值“101011”加權得到:W(博客)=101011 5 = 5 -5 5 -5 5 5,其餘特徵向量類似此般操作。

4. 合併
將上述各個特徵向量的加權結果累加,變成只有一個序列串。拿前兩個特徵向量舉例,例如“CSDN”的“4 -4 -4 4 -4 4”和“博客”的“5 -5 5 -5 5 5”進行累加,得到“4+5 -4±5 -4+5 4±5 -4+5 4+5”,得到“9 -9 1 -1 1 9”。

5. 降維
對於n-bit簽名的累加結果,如果大於0則置1,否則置0,從而得到該語句的simhash值,最後我們便可以根據不同語句simhash的海明距離來判斷它們的相似度。

例如把上面計算出來的“9 -9 1 -1 1 9”降維(某位大於0記爲1,小於0記爲0),得到的01串爲:“1 0 1 0 1 1”,從而形成它們的simhash簽名。

海明距離

Hamming Distance,又稱漢明距離,在信息論中,兩個等長字符串之間的漢明距離是兩個字符串對應位置的不同字符的個數。也就是說,它就是將一個字符串變換成另外一個字符串所需要替換的字符個數。例如:1011101 與 1001001 之間的漢明距離是 2。

我們常說的字符串編輯距離則是一般形式的漢明距離。如此,通過比較多個文檔的simHash值的海明距離,可以獲取它們的相似度。

海明距離計算

  1. 假設獲取兩段文本A的Simhash(A)=100111和B的Simhash(B)=101010
  2. 兩者的海明距離爲hamming_distance(A, B) = count_1(A xor B) = count_1(001101) =

A和B的海明距離是否小於等於n,這個n值根據經驗一般取值爲3,小於等於3說明文本相似性很高

大規模數據海明距離計算
在大規模數據量的情況下,如果對兩個文本64位的SimHash的海明距離採用每一位比較的方法進行計算,找出海明距離小於等於3的文本,這樣會耗費大量時間和資源。

那麼如何在海量的樣本庫中查詢與其海明距離在3以內的記錄呢?

  1. 一種方案是查找待查詢文本的64位simhash code的所有3位以內變化的組合
  2. 另一種方案是預生成庫中所有樣本simhash code的3位變化以內的組合
    這兩種方案,要麼時間複雜度高,要麼空間複雜度複雜,能否有一種方案可以達到時空複雜度的絕佳平衡呢?

一種較好的來均衡計算海明距離的時間複雜度和空間複雜度的算法思路是:

  1. 把64位的SimHash分成四個part,如果兩個SimHash相似(海明距離小於等於3),根據鴿巢原理,必然有一個part是完全相同的。
  2. 如果已存在一個對應part相同,則再進行part中的海明距離計算

SimHash應用

每篇文檔得到SimHash簽名值後,接着計算兩個簽名的海明距離即可。
根據經驗值,對64位的SimHash值,海明距離在3以內的可認爲相似度比較高。

如舉例比較多個文檔中的內容

  1. 將Doc進行關鍵詞抽取(其中包括分詞和計算權重),抽取出n個(關鍵詞,權重)對, 即圖中的多個(feature, weight)。記爲 feature_weight_pairs = [fw1, fw2 … fwn],其中 fwn = (feature_n,weight_n)。

  2. 對每個feature_weight_pairs中的feature進行hash。然後對hash_weight_pairs進行位的縱向累加,如果該位是1,則+weight,如果是0,則-weight,最後生成bits_count個數字,大於0標記1,小於0標記0

  3. 最後轉換成一個64位的字節,判斷重複只需要判斷他們的特徵字的距離是不是<n (n根據經驗一般取3),就可以判斷兩個文檔是否相似。
    在這裏插入圖片描述

    兩個文本只有一個字變化時,如果使用普通Hash則會導致兩次的結果發生較大改變,而SimHash的局部敏感特性,會導致只有部分數據發生變化。
    在這裏插入圖片描述

GeoHash函數

geohash是由Gustavo Niemeyer發明的一套空間地理信息編碼系統,能夠將一個地理位置的經緯度信息轉化爲一串較短的數字和字母組成的字符串。geohash是一個層級空間數據結構,通過使用“Z型曲線”能夠將空間劃分到網格狀的桶(buckets)中,一般也稱爲”空間填充曲線“。

可以理解geohash是一種算法思想,geohash就是把二維的座標點,用一串字符串表示,這樣所有的元素都將在掛載到一條線上,距離靠近的二維座標映射到一維後的點之間距離也會很接近。通過比較geohash值得相似程度來查找附近目標要素。

geohash基本原理是將地球理解爲一個二維平面,將平面遞歸分解成更小的子塊,每個子塊在一定經緯度範圍內擁有相同的編碼,這種方式簡單粗暴,可以滿足對小規模的數據進行經緯度的檢索。

3. GeoHash使用示例

地球緯度區間是[-90,90],上海大寧國際廣場座標(121.458797,31.280291)。

1. 編碼維度
大寧國際廣場的緯度是31.280291,可以通過下面算法對緯度31.280291進行逼近編碼:

  • 1)區間[-90,90]進行二分爲[-90,0),[0,90],稱爲左右區間,可以確定39.928167屬於右區間[0,90],給標記爲1;
  • 2)接着將區間[0,90]進行二分爲 [0,45),[45,90],可以確定31.280291屬於左區間 [0,45),給標記爲0;
  • 3)遞歸上述過程31.280291總是屬於某個區間[a,b]。隨着每次迭代區間[a,b]總在縮小,並越來越逼近31.280291;
  • 4)如果給定的緯度x(31.280291)屬於左區間,則記錄0,如果屬於右區間則記錄1,這樣隨着算法的進行會產生一個序列1010110001,序列的長度跟給定的區間劃分次數有關。 | Column 1
bit min mid max
1 -90.000 0.000
0 0.000 45.000 90.000
1 0.000 22.500 45.000
0 22.500 33.750 45.000
1 22.500 28.125 33.750
1 28.125 30.9375 33.750
0 30.9375 32.34375 33.750
0 30.9375 31.640625 32.34375
0 30.9375 31.2890625 31.640625
1 30.9375 31.1090625 31.2890625

2.編碼精度
同理,地球經度區間是[-180,180],可以對經度121.458797進行編碼。

bit min mid max
1 -180 0 180
1 0 90 180
0 90 135 180
1 90 112.5 135
0 112.5 123.75 135
1 112.5 118.125 123.75
1 118.125 120.9375 123.75
0 120.9375 122.34375 123.75
0 120.9375 121.640625 122.34375
1 120.9375 121.2890625 121.640625

3.編碼組合
通過上述計算,緯度產生的編碼爲10101 10001,經度產生的編碼爲11010 11001。偶數位放經度,奇數位放緯度(從右往左),把2串編碼組合生成新串:11100 11001 11100 00011。

最後使用用0-9、b-z(去掉a, i, l, o)這32個字母進行base32編碼,首先將11100 11001 11100 00011轉成十進制,對應着28、25、28、3,十進制對應的編碼就是wtw3。

Decimal 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Base32 0 1 2 3 4 5 6 7 8 9 b c d e f g
Decimal 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Base32 h j k m n p q r s t u v w x y z

同理,將編碼轉換成經緯度的解碼算法與之相反,對於附近的點即與wtw3進行比較,相近則內容會相近。

GeoHash優缺點

優點:
1、利用一個字段,即可存儲經緯度;搜索時,只需一條索引,效率較高
2、編碼的前綴可以表示更大的區域,查找附近的,非常方便。 SQL中,LIKE ‘wm3yr3%’,即可查詢附近的所有地點。
3、通過編碼精度可模糊座標、隱私保護等。

缺點: 距離和排序需二次運算(篩選結果中運行,其實挺快)

博客書寫不易,您的點贊收藏是我前進的動力,千萬別忘記點贊、 收藏 ^ _ ^ !

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章