萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

一致性哈希與 Solr 分布式搜尋架構:海量資料路由、擴容遷移與工程實踐

背景:從垂直擴展到水平擴展

單機資料庫/搜尋引擎遇到瓶頸時,面臨兩條路:

  • 垂直擴展:升級硬體 (CPU/RAM/SSD) → 成本指數級上升、單點風險
  • 水平擴展:增加廉價節點 → 線性擴容、故障隔離、成本可控

水平擴展核心難題:**資料如何均勻路由到各節點?節點增減時如何最小化資料遷移?**

傳統取模分片的致命缺陷

node = hash(key) % N

當節點數 N 變為 N+1,**幾乎所有鍵的映射關係改變**,導致全量資料遷移 (如 9 個鍵從 2 節點遷移到 3 節點,僅 1 個留在原處)。億級資料遷移需數小時甚至數天,期間系統負載極高、服務不可用。

一致性哈希:環狀空間 + 順時針尋找

核心步驟

  1. 構建 2^32 虛擬環 (0 ~ 42 億)
  2. 節點 (IP/hostname) 經哈希映射到環上
  3. 資料鍵同樣哈希映射到環
  4. 資料順時針尋找第一個節點 → 歸屬該節點

新增節點影響範圍

新節點僅接管「從前一節點到自己」順時針弧段上的資料,**其他節點資料完全不動**。

刪除節點同理:其資料順時針遷移至下一節點。

虛擬節點解決傾斜

物理節點少時 (如 3 台),哈希分布不均導致資料傾斜。
解法:每台物理機映射 多個虛擬節點 (如 3 台 × 100 = 300 虛擬節點),資料分布更均勻。

工程實踐:自建 Solr 分布式叢集 (棄用 SolrCloud/ZooKeeper)

架構圖層次

+------------------+     +------------------+     +------------------+
|   寫入/建立節點   |     |   讀取/查詢節點   |     |   備份/災備節點   |
|  (Index Writer)  |     |  (Query Replicas) |     |  (Backup Replicas)|
+------------------+     +------------------+     +------------------+
         |                         |                         |
         +-----------+-------------+-------------+-----------+
                     |                           |
              +------v------+             +------v------+
              | 一致性哈希   |             | 叢集管理服務 |
              | 路由器       |             | (狀態/告警/熔斷)|
              +-------------+             +-------------+

關鍵設計決策

  • 捨棄 SolrCloud/ZooKeeper:測試 20 筆資料寫入需數十秒,效能不符預期。
  • 自管路由層:應用層/網關層內嵌一致性哈希客戶端,決定寫入/查詢目標 Solr 節點。
  • 讀寫分離:寫入節點專責建立/更新索引;查詢節點掛載唯讀副本,互不干擾。
  • 備份節點:定期快照,寫入節點故障時快速提升備份節點接管。
  • 業務分片:用戶、微博、商品等不同業務建立獨立叢集,避免單一大叢集故障域過大。

程式碼層級實作要點 (Java 範例)

public class ConsistentHashRouter {
    private final SortedMap circle = new TreeMap<>();
    private final int virtualNodes;

    public ConsistentHashRouter(List nodes, int virtualNodes) {
        this.virtualNodes = virtualNodes;
        for (String node : nodes) addNode(node);
    }

    private void addNode(String node) {
        for (int i = 0; i < virtualNodes; i++) {
            long hash = hash(node + "#VN" + i);
            circle.put(hash, node);
        }
    }

    public String getNode(String key) {
        if (circle.isEmpty()) return null;
        long hash = hash(key);
        SortedMap tail = circle.tailMap(hash);
        long targetHash = tail.isEmpty() ? circle.firstKey() : tail.firstKey();
        return circle.get(targetHash);
    }

    private long hash(String key) {
        // MurmurHash3 / FNV1a / MD5 取前 8 bytes
        return Hashing.murmur3_32().hashUnencodedChars(key).asLong() & 0xffffffffL;
    }
}

避坑指南

  • **熱鍵問題**:極熱門鍵 (如大促商品) 仍會集中單節點 → 需應用層本地快取或拆分熱鍵。
  • **節點權重異質**:新舊機器效能不同 → 虛擬節點數按權重分配 (新機 200、舊機 50)。
  • **叢集變更原子性**:路由表更新需版本化、灰度發布,避免瞬間路由不一致導致讀寫錯節點。
  • **監控指標**:各節點 QPS、延遲、資料量傾斜度、遷移進度、錯誤率。
copyright © 萬盛學電腦網 all rights reserved