一文搞懂一致性 hash 的原理和實現
在 go-zero 的分散式快取系統分享裡,Kevin 重點講到過一致性 hash 的原理和分散式快取中的實踐。本文來詳細講講一致性 hash 的原理和在 go-zero 中的實現。
以儲存為例,在整個微服務系統中,我們的儲存不可能說只是一個單節點。
- 一是為了提高穩定,單節點當機情況下,整個儲存就面臨服務不可用;
- 二是資料容錯,同樣單節點資料物理損毀,而多節點情況下,節點有備份,除非互為備份的節點同時損毀。
那麼問題來了,多節點情況下,資料應該寫入哪個節點呢?
hash
所以本質來講:我們需要一個可以將輸入值 “壓縮” 並轉成更小的值,這個值通常狀況下是唯一、格式極其緊湊的,比如 uint64:
- 冪等:每次用同一個值去計算 hash 必須保證都能得到同一個值
這個就是 hash
演算法完成的。
但是採取普通的 hash
演算法進行路由,如:key % N
。有一個節點由於異常退出了叢集或者是心跳異常,這時再進行 hash route
,會造成大量的資料重新 分發
到不同的節點 。節點在接受新的請求時候,需要重新處理獲取資料的邏輯:如果是在快取中,容易引起 快取雪崩。
此時就需要引入 consistent hash
演算法了。
consistent hash
我們來看看 consistent hash
是怎麼解決這些問題的:
rehash
先解決大量 rehash
的問題:
如上圖,當加入一個新的節點時,影響的 key 只有 key31
,新加入(剔除)節點後,只會影響該節點附近的資料。其他節點的資料不會收到影響,從而解決了節點變化的問題。
這個正是:單調性。這也是 normal hash
演算法無法滿足分散式場景的原因。
資料傾斜
其實上圖可以看出:目前多數的 key 都集中在 node 1
上。如果當 node 數量比較少的情況下,可以回引發多數 key 集中在某個 node
上,監控時發現的問題就是:節點之間負載不均。
為了解決這個問題,consistent hash
引入了 virtual node
的概念。
既然是負載不均,我們就人為地構造一個均衡的場景出來,但是實際 node 只有這麼多。所以就使用 virtual node
劃分割槽域,而實際服務的節點依然是之前的 node。
具體實現
先來看看 Get()
:
Get
先說說實現的原理:
- 計算
key
的 hash - 找到第一個匹配的
virtual node
的 index,並取到對應的h.keys[index]
:virtual node hash 值 - 對應到這個
ring
中去尋找一個與之匹配的actual node
其實我們可以看到 ring
中獲取到的是一個 []node
。這是因為在計算 virtual node hash
,可能會發生 hash 衝突,不同的 virtual node hash
對應到一個實際 node。
這也說明:node
與 virtual node
是一對多的關係。而裡面的 ring
就是下面這個設計:
這個其實也就表明了一致性 hash 的分配策略:
-
virtual node
作為值域劃分。key
去獲取node
,從劃分依據上是以virtual node
作為邊界 -
virtual node
通過hash
,在對應關係上保證了不同的 node 分配的 key 是大致均勻的。也就是 打散繫結 - 加入一個新的 node,會對應分配多個
virtual node
。新節點可以負載多個原有節點的壓力,從全域性看,較容易實現擴容時的負載均衡。
Add Node
看完 Get
其實大致就知道整個一致性 hash 的設計:
type ConsistentHash struct {
hashFunc Func // hash 函式
replicas int // 虛擬節點放大因子
keys []uint64 // 儲存虛擬節點hash
ring map[uint64][]interface{} // 虛擬節點與實際node的對應關係
nodes map[string]lang.PlaceholderType // 實際節點儲存【便於快速查詢,所以使用map】
lock sync.RWMutex
}
好了這樣,基本的一個一致性 hash 就實現完備了。
具體程式碼:https://github.com/tal-tech/go-zero/blob/master/core/hash/consistenthash.go
使用場景
開頭其實就說了,一致性 hash 可以廣泛使用在分散式系統中:
- 分散式快取。可以在
redis cluster
這種儲存系統上構建一個cache proxy
,自由控制路由。而這個路由規則就可以使用一致性 hash 演算法 - 服務發現
- 分散式排程任務
以上這些分散式系統中,都可以在負載均衡模組中使用。
專案地址
https://github.com/tal-tech/go-zero
歡迎使用 go-zero 並 star 支援我們!
微信交流群
關注『微服務實踐』公眾號並點選 交流群 獲取社群群二維碼。
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- 一文搞懂一致性hash的原理和實現
- 一致性Hash的原理與實現
- 強一致性hash實現java版本及強一致性hash原理Java
- 一文講透一致性雜湊的原理和實現
- 一文搞懂服務註冊發現的原理與實現
- 一致性hash演算法原理及go實現演算法Go
- 給面試官講明白:一致性Hash的原理和實踐面試
- 一文搞懂Zookeeper原理
- 一文搞懂基於zipkin的分散式追蹤系統原理與實現分散式
- 徹底搞懂 Channel 實現原理
- 一致性hash的c++簡單實現C++
- YC一文搞懂MySQL持久化和回滾的原理bmyMySql持久化
- 【策略】一致性Hash演算法(Hash環)的java程式碼實現演算法Java
- 一文搞懂AQS及其元件的核心原理AQS元件
- 一文搞定HashMap的實現原理和麵試HashMap
- 深入淺出一致性Hash原理
- 一文搞懂影片編解碼原理
- 瀏覽器渲染原理(一文搞懂)瀏覽器
- 一文帶你搞懂 CDN 的技術原理
- 一文搞懂如何實現 Go 超時控制Go
- 手動實現一致性 Hash 演算法演算法
- 資料庫實現原理#4(Hash Join)資料庫
- consistent hash 原理,優化及實現優化
- 自己實現一個一致性 Hash 演算法演算法
- IM的掃碼登入功能如何實現?一文搞懂主流的掃碼登入技術原理
- 搞懂MySQL InnoDB事務ACID實現原理MySql
- 一文講透自適應熔斷的原理和實現
- 一文搞懂MySQL事務的隔離性如何實現|MVCCMySqlMVC
- Golang 實現 Redis(7): Redis 叢集與一致性 HashGolangRedis
- Java實現一致性Hash演算法深入研究Java演算法
- 一文搞懂 ZigZag 演算法及 Go 語言的實現演算法Go
- 一文搞懂 CountDownLatch 用法和原始碼!CountDownLatch原始碼
- 對一致性Hash演算法,Java程式碼實現的深入研究演算法Java
- 一致性hash原理 看這一篇就夠了
- 部落格建站6 - 一文搞懂域名解析(保姆級教程和原理講解)
- 一文搞懂Session和Cookie的用法及區別SessionCookie
- 設計模式篇之一文搞懂如何實現單例模式設計模式單例
- 一致性 Hash 演算法的實際應用演算法