多個ElasticSearch Cluster的一致性問題

本篇討論同時使用多個ES Cluster進行搜索的時候,如何保證數據的一致性。

? 名詞解釋

Cluster:集群,一個集群包含多個Node,且會有一個Master Node。

Node:節點,一般來說一個機器部署一個Node。

Shard:分片,指的是一個Index分成多少份,這些Shards會分散到各個Node上面。

? 為什麼要使用多個ES Cluster?

高可用方面:

ElastcSearch擁有許多高可用的特性,例如Replica,例如Data Node掛掉後的數據遷移,例如Master Node掛掉後的自動重選,但這不代表萬無一失了。常見的坑是,某個Node表現糟糕但是偏偏又沒掛(掛了反而更好),此時整個Cluster的性能就會被一個坑爹Node拖累,這往往就是雪崩的開始。因此,從高可用方面來考慮,應當部署多個ES Cluster(部分作為災備)。

性能方面:

單個Cluster的搜索能力是有瓶頸的。Cluster越大,Node越多,自然Shard就越多。而Shard不是越多越好,Shard增多會導致通訊成本的增加、查詢收束時Re-ranking環節的負擔增加。如果有100台機器,那麼比起一個100 Node、300 Shards的巨型Cluster,使用十個10 Node 30 Shards的小型Cluster可能表現會更好。

? 什麼叫多個ElastcSearch Cluster的一致性問題?

本篇討論的就是使用多個小型Cluster、而不是一個巨大Cluster進行搜索時會出現的問題。

假設部署了兩個Cluster,記為Cluster A和Cluster B,請求均攤去兩個Cluster。有一天,你對兩個Cluster同時新增了一條數據,但由於一些網路延遲之類的理由,A已經添加了但是B還沒有,這時候一個用戶搜索請求進來,可能會出現這麼個情況:

如果請求的pageSize=4。page=1時,用戶請求去了Cluster B;page=2時,用戶請求去了Cluster A。這樣,用戶會看到以下的結果:

用戶:黑人問號.jpg

更糟糕的是,萬一在ES前面有一層緩存擋著,而緩存不巧記錄了這個詭異的結果,那影響就會擴大了(所有用戶:黑人問號.jpg)。

? 解決方案A:單點大法

1. 進入業務請求低谷期時,把所有請求切去1個ES Cluster;

2. 所有ES Cluster開始進行數據同步;

3. 同步完畢後,同時準備離開請求低谷期了,請求開始均攤到多個ES Cluster上。

優點:簡單粗暴就是美。能簡單粗暴解決的,就不要套複雜的東西。

缺點:1)每天高峰期不能新增數據;2)必須要在低谷期內完成所有數據同步,萬一數據同步流程很長且時間不可控則很難實現;3)單點要能夠頂過低谷期,萬一流量判斷錯誤、或者被攻擊,導致單點崩潰,可能發生嚴重事故。

? 解決方案B:切別名大法

多個ElastcSearch Index的名字切換是個原子操作(搜索"elasticsearch alias"),所以可以這樣:

1. 創建兩個一樣的Index(記為A1、A2);

2. 同步數據到A1;

3. 同步完後,設置別名A,指向剛剛同步好數據的A1(記為A->A1);

4. 使用A進行搜索,請求均攤到每個ES Cluster上;

5. 每次Re-load的時候,所有ES Cluster將數據更新到那個待機中的Index(例如A->A1,那麼就更新A2,反之亦然)。在所有ES Cluster都完成數據更新後,同時切換別名(如A->A1,則A->A2,反之亦然)。

優點:比方案A優雅多了。

缺點:1)每個Index要創建兩份,存儲成本翻倍;2)跟方案A一樣,不能實時添加數據;3)實際上,只要是均攤請求,就會出現不一致的問題,ElasticSearch可能根據分片的不同會出現不同的得分,不應使用均攤。

? 解決方案C:哈希大法

1. 對請求進行哈希/散列,確保一個請求每次都會去到同一個ES Cluster;

2. 每個ES Cluster該幹嘛幹嘛。

優點:比方案B優雅多了,還能實時添加數據。

缺點:萬一其中一個ES Cluster掛掉了,怎麼辦?均攤請求的做法可以很輕鬆地將掛掉的ES Cluster整個踢出去,那哈希法呢?

? 解決方案D:一致性哈希大法

終於還是到了這一招,新增一層虛擬層,將每個ES Cluster抽象成一個環上的虛擬節點。每個請求在哈希後,先映射去虛擬層,再映射去真實的ES Cluster。

由於每個ES Cluster都存儲了完整的數據拷貝,我們並不需要考慮一致性哈希的數據遷移問題。每次新增/刪除ES Cluster,就重新分配虛擬節點的位置(環上均分),就可以了。

? 一個新的問題:如何確保多個ES Cluster的更新操作的一致性?

上述全文都在討論搜索的一致性,那麼如何保證插入/更新的一致性呢?

我的解法是,加入一層可以被多人重複消費的消息隊列(例如Kafka),作為所有ES Cluster插入/更新的中間層。

這個方案的好處是:

1)主更新程序只有一個,提高可控性和發現問題的能力;

2)使用消息隊列來統一發布內容,降低了對數據源的壓力;

3)圖中消費者這個角色,Elastic Stack官方提供了一個輕量級高可用解決方案,就是Beat

? 最後留一個小問題

前文討論了多個Cluster+緩存時出現的一致性問題,其實單Cluster+緩存也可能出現這個問題(極少就是了)。那麼,有沒有辦法徹底解決這個問題呢?

? 結語

ElasticSearch本身是個分散式系統,但如果將其作為一個更大的分散式系統的一個單元的話,將會出現什麼問題呢?本文希望可以通過循序漸進的方法,分析圍繞ES的高可用方案設計,有許多問題是分散式系統里常見的問題,希望可以對讀者有所啟發。

推薦閱讀:

Elastic Stack 5.0升級踩坑記
elasticsearch狀態顯示為red,該如何修復索引呢?
學習elasticsearch必須先學習lucene嗎?
elasticsearch,我用ik分詞,搜索"寶馬2012",怎樣只查出即包含「寶馬」又包含「2012」的文章?

TAG:Elasticsearch | 搜索引擎 | 并发并行与分布式系统 |