說到FNV哈希算法不得不提Memcached,咱們先簡單介紹一下Memcached。git
Memcached
分爲客戶端與服務端,Memcached
是服務端,服務端自己不提供分佈式實現,只是一個單獨的k-v緩存;Memcached的分佈式是在客戶端類庫中實現的,也就是說你能夠根據本身的須要實現不一樣的分佈式方案,不必定非得使用FNV哈希算法。github
Memcached經過FNV算法實現了服務的分佈式,並經過引入虛擬節點的辦法儘可能是服務器分佈的更均勻。已經有不少文章在介紹Memcached的分佈式實現原理了,因此我就不那麼多廢話了。算法
若是你還不瞭解FNV哈希算法,能夠先看一下我以前的文章,在那裏我摘錄了wiki上的FNV哈希算法實現公式。c#
代碼實現上我將參考MD5算法的實現來編寫FNV1算法:數組
首先,我將建立一個FNV1類,該類須要實現HashAlgorithm,之因此實現HashAlgorithm,是由於該抽象類定義了hash算法通用的接口,這樣也可使咱們的實現與.net框架集成的更好,固然若是你不喜歡也能夠不實現HashAlgorithm,就當是寫了一個獨立的幫助類。緩存
而後,咱們重寫Create方法,這裏咱們將建立一個FNV1類的實例服務器
最後,咱們去實現這個FNV1類框架
全部實現代碼以下:
分佈式
//首先我將建立FNV1類 public abstract class FNV1 : HashAlgorithm { //重寫隱藏HashAlgorithm的Create方法 public static new FNV1 Create() { return new Implementation(); } //下面FNV1的實現咱們徹底是套用的公式沒有什麼好講的 private sealed class Implementation : FNV1 { private const uint OFFSETBASIS = 2166136261; private const uint PRIME = 16777619; private uint _hash; public override void Initialize() { _hash = OFFSETBASIS; } protected override void HashCore(byte[] array, int ibStart, int cbSize) { int end = ibStart + cbSize; for (var index = ibStart; index < end; index++) { _hash *= PRIME; _hash ^= array[index]; } } protected override byte[] HashFinal() { return BitConverter.GetBytes(_hash); } } } ## 使用方法 var bytes=Encoding.UTF8.GetBytes("Test"); var hashBytes=FNV1.Create().ComputerHash(bytes); var hashValue=BitConverter.ToUInt32(hashBytes);
FNV其實還有FNV1a算法,與FNV1有些許的區別,這裏我就不一一實現了,你能夠參考FNV1的實現和FNV哈希算法來實現FNV1a算法。我有一個幫助類MicroFx.Cryptography分別實現了FNV1和FNV1a的32bit、64bit算法版本。
不管是分佈式算法仍是hash一致性算法都不僅有一種或幾種實現方案,但Memached爲何會選擇FNV算法,而不是md5,不是sha呢?我有本身的認識。
咱們先看幾行代碼,分別使用MD5,sha,FNV算法計算一個Test
字符串的哈希值,而後對比hash結果中數組的長度
var bytes = Encoding.UTF8.GetBytes("Test"); var shabytes = SHA1.Create().ComputeHash(bytes); //shabytes長度爲20,及160bit var md5bytes=MD5.Create().ComputeHash(bytes); //md5bytes長度爲16,及128bit var fnvbytes = FNV1a.Create().ComputeHash(bytes); //fnvbytes長度爲4,及32bit
算法 | 取值範圍 |
---|---|
sha1 | [0,2^160-1] |
md5 | [0,2^128-1] |
fnv | [0,2^32-1] |
從上表咱們能夠看出,FNV的取值範圍最小,若是將區間內的每個整數看作一個Memcached服務端節點,那麼FNV容納的數量最少,但相對於實際的環境下已經足夠多了,這樣咱們每次在計算一臺服務器屬於哪一個節點的時候速度上會比md五、sha1快不少。
FNV的32bit計算結果值恰好是一個uint類型,.net core最大支持ulong也就是uint64,再大的話就須要咱們本身實現,因此這也是選擇FNV的一個緣由。(或許我這裏不該該拿.net舉例,但實際經常使用的高級語言最大也是64bit)