什麼叫一致性雜湊,通常用來解決什麼問題?
這裡是修真院後端小課堂,每篇分享文從
【背景介紹】【知識剖析】【常見問題】【解決方案】【編碼實戰】【擴充套件思考】【更多討論】【參考文獻】
八個方面深度解析後端知識/技能,本篇分享的是:
【 什麼叫一致性雜湊,通常用來解決什麼問題?】
【修真院Java小課堂】什麼叫一致性雜湊,通常用來解決什麼問題?
大家好,我是IT修真院北京分院第35期的學員趙君釗,一枚正直純潔善良的Java程式設計師,今天給大家分享一下,修真院官網Java(職業)任務六,深度思考中的知識點——什麼叫一致性雜湊,通常用來解決什麼問題?
1. 背景介紹
在瞭解一致性雜湊演算法之前,先了解一下一致性雜湊演算法的應用場景,在做快取叢集時,為了緩解伺服器的壓力,會部署多臺快取伺服器,把資料資源均勻的分配到每個伺服器上,分散式資料庫首先要解決把整個資料集按照分割槽規則對映到多個節點的問題,即把資料集劃分到多個節點上,每個節點負責整體資料的一個子集。
資料分佈通常有雜湊分割槽和順序分割槽兩種方式
順序分佈:資料分散度易傾斜、鍵值業務相關、可順序訪問、不支援批量操作
雜湊分佈:資料分散度高、鍵值分佈業務無關、無法順序訪問、支援批量操作
2. 知識剖析
節點取餘分割槽
普通雜湊演算法,使用特定的資料,如Redis的鍵或使用者ID,再根據節點數量N使用公式:hash(key)% N 計算出雜湊值,用來決定資料對映到哪一個節點上。
優點
這種方式的突出優點是簡單性,常用於資料庫的分庫分表規則。一般採用預分割槽的方式,提前根據資料量規劃好分割槽數
缺點
當節點數量變化時,如擴容或收縮節點,資料節點對映關係需要重新計算,會導致資料的重新遷移。所以擴容時通常採用翻倍擴容,避免 資料對映全部被打亂,導致全量遷移的情況,這樣只會發生50%的資料遷移。
一致性雜湊分割槽
一致性雜湊的目的就是為了在節點數目發生改變時儘可能少的遷移資料,將所有的儲存節點排列在收尾相接的Hash環上,每個key在計算Hash 後會順時針找到臨接的儲存節點存放。而當有節點加入或退 時,僅影響該節點在Hash環上順時針相鄰的後續節點。
優點
加入和刪除節點隻影響雜湊環中順時針方向的相鄰的節點,對其他節點無影響。
缺點
資料的分佈和節點的位置有關,因為這些節點不是均勻的分佈在雜湊環上的,所以資料在進行儲存時達不到均勻分佈的效果。
虛擬槽分割槽
本質上還是第一種的普通雜湊演算法,把全部資料離散到指定數量的雜湊槽中,把這些雜湊槽按照節點數量進行了分割槽。這樣因為雜湊槽的數量的固定的,新增節點也不用把資料遷移到新的雜湊槽,只要在節點之間互相遷移就可以了,即保證了資料分佈的均勻性,又保證了在新增節點的時候不必遷移過多的資料。
Redis的叢集模式使用的就是虛擬槽分割槽,一共有16383個槽位平均分佈到節點上
3.常見問題
4.解決方案
5.編碼實戰
普通雜湊演算法
public void hashTest() { int[] node = new int[100]; for(Integer i = 0;i < NUM;i++){ Integer h = MD5Util.encrypt(i.toString(),"").hashCode(); int index = Math.abs(h) % 100; node[index]++; } int max = node[0]; int min = node[0]; for(int i : node){ if (max < i){ max = i; } if (min > i){ min = i; } } Arrays.stream(node).forEach(logger::info); System.out.println("max :" + max); System.out.println("min :" + min); }
節點變化時資料進行遷移
public void hashDiff() { int diff = 0; for(Integer i = 0;i < NUM;i++){ Integer h = MD5Util.encrypt(i.toString(),"").hashCode(); int index = Math.abs(h) % 100; int new_index = Math.abs(h) % 101; if(new_index != index){ diff++; } } System.out.println("diff : " + diff); }
一致性雜湊演算法
public void consistentHash() { int[] node = new int[100]; int[] nodeHash = new int[100]; for(int i = 0; i < 100; i++) { int h = MD5Util.encrypt(Integer.toString(i), "").hashCode(); nodeHash[i] = Math.abs(h); } qSort(nodeHash, 0, nodeHash.length - 1); for(int i = 0; i < NUM; i++){ int flag = 0; int h = Math.abs(MD5Util.encrypt(Integer.toString(i), "").hashCode()); for (int j = 0; j < nodeHash.length; j++){ if(h <= nodeHash[j]){ node[j]++; flag = 1; break; } } if(flag == 0){ node[0]++; } } Arrays.stream(node).forEach(logger::info); System.out.println("max :" + max); System.out.println("min :" + min); }
虛擬槽分割槽
public void clusterHash() { int[] node = new int[100]; int[] nodeSlot = new int[100]; for (int i = 0; i < 100; i++){ // nodeSlot儲存節點負責的雜湊槽範圍 nodeSlot[i] = cluster / node.length * (i + 1); } for(int i = 0; i < NUM; i++){ int h = MD5Util.encrypt(Integer.toString(i), "").hashCode(); int index = Math.abs(h) % cluster; for(int j = 0; j < nodeSlot.length; j++){ if(index <= nodeSlot[j]){ node[j]++; break; } } } Arrays.stream(node).forEach(logger::info); System.out.println("max :" + max); System.out.println("min :" + min); }
6.擴充套件思考
7.參考文獻
參考資料:https://juejin.im/post/5b8fc5536fb9a05d2d01fb11
————深入剖析Redis系列(三) - Redis叢集模式搭建與原理詳解
8.更多討論
一致性雜湊演算法會有雜湊衝突嗎?
不衝突的雜湊演算法是不存在的,但是隻要虛擬節點夠多,保證在概率上每個真實節點的負載是相等的就好了,一致性雜湊的雜湊環有細虛擬的2^32個節點。
一致性雜湊的雜湊槽數為什麼是2^32
一致性雜湊一定程度上也解決了雜湊衝突,只要雜湊槽的範圍足夠大就能儘可能的減少雜湊衝突,因為通常的hashCode都是將資料對映到0 ~ 2^32 數值空間內,所以設定一個2^32個節點的雜湊環會盡可能的減少雜湊衝突。
有沒有其他解決節點變化時資料遷移的方法?
實際上一致性雜湊是把可變的雜湊槽固定到雜湊環上,整數最大值2^32個槽位,所以一致性雜湊的本質已經不是節點取模了,每個資料的位置是固定的,只要能保證節點數變化時減少key在節點之間的重對映就可以,比如說虛擬槽分割槽。
9.鳴謝
感謝觀看,如有出錯,懇請指正
10.結束語
今天的分享就到這裡啦,歡迎大家點贊、轉發、留言、拍磚~
更多內容,可以加入IT交流群565734203與大家一起討論交流
這裡是技能樹·IT修真院:https://www.jnshu.com,初學者轉行到網際網路的聚集地