架構原理
本書作為 Elastic Stack 指南劲室,關注于 Elasticsearch 在日志和數(shù)據(jù)分析場景的應用,并不打算對底層的 Lucene 原理或者 Java 編程做詳細的介紹获搏,但是 Elasticsearch 層面上的一些架構設計,對我們做性能調(diào)優(yōu)蜓肆,故障處理颜凯,具有非常重要的影響。
所以仗扬,作為 ES 部分的起始章節(jié)症概,先從數(shù)據(jù)流向和分布的層面,介紹一下 ES 的工作原理早芭,以及相關的可控項彼城。各位讀者可以跳過這節(jié)先行閱讀后面的運維操作部分,但作為性能調(diào)優(yōu)的基礎知識退个,依然建議大家抽時間返回來了解募壕。
帶著問題學習
- 寫入的數(shù)據(jù)是如何變成elasticsearch里可以被檢索和聚合的索引內(nèi)容的?
- lucene如何實現(xiàn)準實時索引语盈?
- 什么是segment舱馅?
- 什么是commit?
- segment的數(shù)據(jù)來自哪里刀荒?
- segment在寫入磁盤前就可以被檢索代嗤,是因為利用了什么?
- elasticsearch中的refresh操作是什么缠借?配置項是哪個干毅?設置的命令是什么?
- refresh只是寫到了文件系統(tǒng)緩存泼返,那么實際寫入磁盤是由什么控制呢硝逢?,如果這期間發(fā)生錯誤和故障绅喉,數(shù)據(jù)會不會丟失渠鸽?
- 什么是translog日志?什么時候會被清空柴罐?什么是flush操作拱绑?配置項是什么?怎么配置丽蝎?
10.什么是段合并猎拨?為什么要段合并膀藐?段合并線程配置項?段合并策略红省?怎么forcemerge(optimize)额各? - routing的規(guī)則是什么樣的?replica讀寫過程吧恃?wait_for_active_shards參數(shù)timeout參數(shù) 虾啦?
- reroute 接口?
- 兩種 自動發(fā)現(xiàn)方式痕寓?
目錄
segment傲醉、buffer和translog對實時性的影響
既然介紹數(shù)據(jù)流向,首先第一步就是:寫入的數(shù)據(jù)是如何變成 Elasticsearch 里可以被檢索和聚合的索引內(nèi)容的呻率?
以單文件的靜態(tài)層面看硬毕,每個全文索引都是一個詞元的倒排索引,具體涉及到全文索引的通用知識礼仗,這里不單獨介紹吐咳,有興趣的讀者可以閱讀《Lucene in Action》等書籍詳細了解。
動態(tài)更新的 Lucene 索引
以在線動態(tài)服務的層面看元践,要做到實時更新條件下數(shù)據(jù)的可用和可靠韭脊,就需要在倒排索引的基礎上,再做一系列更高級的處理单旁。
其實總結一下 Lucene 的處理辦法沪羔,很簡單,就是一句話:新收到的數(shù)據(jù)寫到新的索引文件里象浑。
Lucene 把每次生成的倒排索引蔫饰,叫做一個段(segment)。然后另外使用一個 commit 文件融柬,記錄索引內(nèi)所有的 segment。而生成 segment 的數(shù)據(jù)來源趋距,則是內(nèi)存中的 buffer粒氧。也就是說,動態(tài)更新過程如下:
-
當前索引有 3 個 segment 可用节腐。索引狀態(tài)如圖 2-1外盯;
A Lucene index with a commit point and three segments圖 2-1
-
新接收的數(shù)據(jù)進入內(nèi)存 buffer。索引狀態(tài)如圖 2-2翼雀;
A Lucene index with new documents in the in-memory buffer, ready to commit圖 2-2
-
內(nèi)存 buffer 刷到磁盤饱苟,生成一個新的 segment,commit 文件同步更新狼渊。索引狀態(tài)如圖 2-3箱熬。
After a commit, a new segment is added to the index and the buffer is cleared圖 2-3
利用磁盤緩存實現(xiàn)的準實時檢索
既然涉及到磁盤类垦,那么一個不可避免的問題就來了:磁盤太慢了!對我們要求實時性很高的服務來說城须,這種處理還不夠蚤认。所以,在第 3 步的處理中糕伐,還有一個中間狀態(tài):
-
內(nèi)存 buffer 生成一個新的 segment砰琢,刷到文件系統(tǒng)緩存中,Lucene 即可檢索這個新 segment良瞧。索引狀態(tài)如圖 2-4陪汽。
The buffer contents have been written to a segment, which is searchable, but is not yet commited圖 2-4
文件系統(tǒng)緩存真正同步到磁盤上,commit 文件更新褥蚯。達到圖 2-3 中的狀態(tài)挚冤。
這一步刷到文件系統(tǒng)緩存的步驟,在 Elasticsearch 中遵岩,是默認設置為 1 秒間隔的你辣,對于大多數(shù)應用來說,幾乎就相當于是實時可搜索了尘执。Elasticsearch 也提供了單獨的 /_refresh
接口舍哄,用戶如果對 1 秒間隔還不滿意的,可以主動調(diào)用該接口來保證搜索可見誊锭。
注:5.0 中還提供了一個新的請求參數(shù):?refresh=wait_for
表悬,可以在寫入數(shù)據(jù)后不強制刷新但一直等到刷新才返回。
不過對于 Elastic Stack 的日志場景來說丧靡,恰恰相反蟆沫,我們并不需要如此高的實時性,而是需要更快的寫入性能温治。所以饭庞,一般來說,我們反而會通過 /_settings
接口或者定制 template 的方式熬荆,加大 refresh_interval
參數(shù):
# curl -XPOST http://127.0.0.1:9200/logstash-2015.06.21/_settings -d'{ "refresh_interval": "10s" }'
- 1
- 2
- 3
如果是導入歷史數(shù)據(jù)的場合舟山,那甚至可以先完全關閉掉:
# curl -XPUT http://127.0.0.1:9200/logstash-2015.05.01 -d'{ "settings" : { "refresh_interval": "-1" }}'
- 1
- 2
- 3
- 4
- 5
- 6
在導入完成以后,修改回來或者手動調(diào)用一次即可:
# curl -XPOST http://127.0.0.1:9200/logstash-2015.05.01/_refresh
- 1
【聲明:轉(zhuǎn)載請注明出處
獨立:http://wangnan.tech
簡書:http://www.reibang.com/u/244399b1d776
CSDN:http://blog.csdn.net/wangnan9279】
translog 提供的磁盤同步控制
既然 refresh 只是寫到文件系統(tǒng)緩存卤恳,那么第 4 步寫到實際磁盤又是有什么來控制的累盗?如果這期間發(fā)生主機錯誤、硬件故障等異常情況突琳,數(shù)據(jù)會不會丟失若债?
這里,其實有另一個機制來控制拆融。Elasticsearch 在把數(shù)據(jù)寫入到內(nèi)存 buffer 的同時蠢琳,其實還另外記錄了一個 translog 日志啊终。也就是說,第 2 步并不是圖 2-2 的狀態(tài)挪凑,而是像圖 2-5 這樣:
圖 2-5
在第 3 和第 4 步孕索,refresh 發(fā)生的時候,translog 日志文件依然保持原樣躏碳,如圖 2-6:
圖 2-6
也就是說搞旭,如果在這期間發(fā)生異常,Elasticsearch 會從 commit 位置開始菇绵,恢復整個 translog 文件中的記錄肄渗,保證數(shù)據(jù)一致性。
等到真正把 segment 刷到磁盤咬最,且 commit 文件進行更新的時候翎嫡, translog 文件才清空。這一步永乌,叫做 flush惑申。同樣,Elasticsearch 也提供了 /_flush
接口翅雏。
對于 flush 操作圈驼,Elasticsearch 默認設置為:每 30 分鐘主動進行一次 flush,或者當 translog 文件大小大于 512MB (老版本是 200MB)時望几,主動進行一次 flush绩脆。這兩個行為,可以分別通過 index.translog.flush_threshold_period
和 index.translog.flush_threshold_size
參數(shù)修改橄抹。
如果對這兩種控制方式都不滿意靴迫,Elasticsearch 還可以通過 index.translog.flush_threshold_ops
參數(shù),控制每收到多少條數(shù)據(jù)后 flush 一次楼誓。
translog 的一致性
索引數(shù)據(jù)的一致性通過 translog 保證玉锌。那么 translog 文件自己呢?
默認情況下疟羹,Elasticsearch 每 5 秒主守,或每次請求操作結束前,會強制刷新 translog 日志到磁盤上阁猜。
后者是 Elasticsearch 2.0 新加入的特性丸逸。為了保證不丟數(shù)據(jù)蹋艺,每次 index剃袍、bulk、delete捎谨、update 完成的時候民效,一定觸發(fā)刷新 translog 到磁盤上憔维,才給請求返回 200 OK。這個改變在提高數(shù)據(jù)安全性的同時當然也降低了一點性能畏邢。
如果你不在意這點可能性业扒,還是希望性能優(yōu)先,可以在 index template 里設置如下參數(shù):
{ "index.translog.durability": "async"}
- 1
- 2
- 3
Elasticsearch 分布式索引
大家可能注意到了舒萎,前面一段內(nèi)容程储,一直寫的是”Lucene 索引”。這個區(qū)別在于臂寝,Elasticsearch 為了完成分布式系統(tǒng)章鲤,對一些名詞概念作了變動。索引成為了整個集群級別的命名咆贬,而在單個主機上的Lucene 索引败徊,則被命名為分片(shard)。至于數(shù)據(jù)是怎么識別到自己應該在哪個分片掏缎,請閱讀稍后有關 routing 的章節(jié)皱蹦。
segment merge對寫入性能的影響
通過上節(jié)內(nèi)容,我們知道了數(shù)據(jù)怎么進入 ES 并且如何才能讓數(shù)據(jù)更快的被檢索使用眷蜈。其中用一句話概括了 Lucene 的設計思路就是”開新文件”沪哺。從另一個方面看,開新文件也會給服務器帶來負載壓力端蛆。因為默認每 1 秒凤粗,都會有一個新文件產(chǎn)生,每個文件都需要有文件句柄今豆,內(nèi)存嫌拣,CPU 使用等各種資源。一天有 86400 秒呆躲,設想一下异逐,每次請求要掃描一遍 86400 個文件,這個響應性能絕對好不了插掂!
為了解決這個問題灰瞻,ES 會不斷在后臺運行任務,主動將這些零散的 segment 做數(shù)據(jù)歸并辅甥,盡量讓索引內(nèi)只保有少量的酝润,每個都比較大的,segment 文件璃弄。這個過程是有獨立的線程來進行的要销,并不影響新 segment 的產(chǎn)生。歸并過程中夏块,索引狀態(tài)如圖 2-7疏咐,尚未完成的較大的 segment 是被排除在檢索可見范圍之外的:
圖 2-7
當歸并完成纤掸,較大的這個 segment 刷到磁盤后,commit 文件做出相應變更浑塞,刪除之前幾個小 segment借跪,改成新的大 segment。等檢索請求都從小 segment 轉(zhuǎn)到大 segment 上以后酌壕,刪除沒用的小 segment掏愁。這時候,索引里 segment 數(shù)量就下降了卵牍,狀態(tài)如圖 2-8 所示:
圖 2-8
歸并線程配置
segment 歸并的過程托猩,需要先讀取 segment,歸并計算,再寫一遍 segment,最后還要保證刷到磁盤彻舰【α眨可以說,這是一個非常消耗磁盤 IO 和 CPU 的任務。所以,ES 提供了對歸并線程的限速機制,確保這個任務不會過分影響到其他任務欠气。
在 5.0 之前,歸并線程的限速配置 indices.store.throttle.max_bytes_per_sec
是 20MB镜撩。對于寫入量較大预柒,磁盤轉(zhuǎn)速較高,甚至使用 SSD 盤的服務器來說袁梗,這個限速是明顯過低的宜鸯。對于 Elastic Stack 應用,社區(qū)廣泛的建議是可以適當調(diào)大到 100MB或者更高遮怜。
# curl -XPUT http://127.0.0.1:9200/_cluster/settings -d'{ "persistent" : { "indices.store.throttle.max_bytes_per_sec" : "100mb" }}'
- 1
- 2
- 3
- 4
- 5
- 6
5.0 開始,ES 對此作了大幅度改進锯梁,使用了 Lucene 的 CMS(ConcurrentMergeScheduler) 的 auto throttle 機制即碗,正常情況下已經(jīng)不再需要手動配置 indices.store.throttle.max_bytes_per_sec
了。官方文檔中都已經(jīng)刪除了相關介紹合敦,不過從源碼中還是可以看到初橘,這個值目前的默認設置是 10240 MB。
歸并線程的數(shù)目,ES 也是有所控制的壁却。默認數(shù)目的計算公式是: Math.min(3, Runtime.getRuntime().availableProcessors() / 2)
。即服務器 CPU 核數(shù)的一半大于 3 時裸准,啟動 3 個歸并線程展东;否則啟動跟 CPU 核數(shù)的一半相等的線程數(shù)。相信一般做 Elastic Stack 的服務器 CPU 合數(shù)都會在 6 個以上炒俱。所以一般來說就是 3 個歸并線程盐肃。如果你確定自己磁盤性能跟不上,可以降低 index.merge.scheduler.max_thread_count
配置权悟,免得 IO 情況更加惡化砸王。
歸并策略
歸并線程是按照一定的運行策略來挑選 segment 進行歸并的。主要有以下幾條:
- index.merge.policy.floor_segment
默認 2MB峦阁,小于這個大小的 segment谦铃,優(yōu)先被歸并。 - index.merge.policy.max_merge_at_once
默認一次最多歸并 10 個 segment - index.merge.policy.max_merge_at_once_explicit
默認 forcemerge 時一次最多歸并 30 個 segment榔昔。 - index.merge.policy.max_merged_segment
默認 5 GB驹闰,大于這個大小的 segment,不用參與歸并撒会。forcemerge 除外嘹朗。
根據(jù)這段策略,其實我們也可以從另一個角度考慮如何減少 segment 歸并的消耗以及提高響應的辦法:加大 flush 間隔诵肛,盡量讓每次新生成的 segment 本身大小就比較大屹培。
forcemerge 接口
既然默認的最大 segment 大小是 5GB。那么一個比較龐大的數(shù)據(jù)索引怔檩,就必然會有為數(shù)不少的 segment 永遠存在褪秀,這對文件句柄,內(nèi)存等資源都是極大的浪費薛训。但是由于歸并任務太消耗資源溜歪,所以一般不太選擇加大 index.merge.policy.max_merged_segment
配置,而是在負載較低的時間段许蓖,通過 forcemerge 接口蝴猪,強制歸并 segment。
# curl -XPOST http://127.0.0.1:9200/logstash-2015-06.10/_forcemerge?max_num_segments=1
- 1
由于 forcemerge 線程對資源的消耗比普通的歸并線程大得多膊爪,所以自阱,絕對不建議對還在寫入數(shù)據(jù)的熱索引執(zhí)行這個操作。這個問題對于 Elastic Stack 來說非常好辦米酬,一般索引都是按天分割的沛豌。更合適的任務定義方式,請閱讀本書稍后的 curator 章節(jié)。
routing和replica的讀寫過程
之前兩節(jié)加派,完整介紹了在單個 Lucene 索引叫确,即 ES 分片內(nèi)的數(shù)據(jù)寫入流程。現(xiàn)在徹底回到 ES 的分布式層面上來芍锦,當一個 ES 節(jié)點收到一條數(shù)據(jù)的寫入請求時竹勉,它是如何確認這個數(shù)據(jù)應該存儲在哪個節(jié)點的哪個分片上的?
路由計算
作為一個沒有額外依賴的簡單的分布式方案娄琉,ES 在這個問題上同樣選擇了一個非常簡潔的處理方式次乓,對任一條數(shù)據(jù)計算其對應分片的方式如下:
shard = hash(routing) % number_of_primary_shards
每個數(shù)據(jù)都有一個 routing 參數(shù),默認情況下孽水,就使用其 _id
值票腰。將其 _id
值計算哈希后,對索引的主分片數(shù)取余女气,就是數(shù)據(jù)實際應該存儲到的分片 ID杏慰。
由于取余這個計算,完全依賴于分母炼鞠,所以導致 ES 索引有一個限制逃默,索引的主分片數(shù),不可以隨意修改簇搅。因為一旦主分片數(shù)不一樣完域,所以數(shù)據(jù)的存儲位置計算結果都會發(fā)生改變,索引數(shù)據(jù)就完全不可讀了瘩将。
副本一致性
作為分布式系統(tǒng)吟税,數(shù)據(jù)副本可算是一個標配。ES 數(shù)據(jù)寫入流程姿现,自然也涉及到副本肠仪。在有副本配置的情況下,數(shù)據(jù)從發(fā)向 ES 節(jié)點备典,到接到 ES 節(jié)點響應返回异旧,流向如下(附圖 2-9):
- 客戶端請求發(fā)送給 Node 1 節(jié)點,注意圖中 Node 1 是 Master 節(jié)點提佣,實際完全可以不是吮蛹。
- Node 1 用數(shù)據(jù)的
_id
取余計算得到應該講數(shù)據(jù)存儲到 shard 0 上。通過 cluster state 信息發(fā)現(xiàn) shard 0 的主分片已經(jīng)分配到了 Node 3 上拌屏。Node 1 轉(zhuǎn)發(fā)請求數(shù)據(jù)給 Node 3潮针。 - Node 3 完成請求數(shù)據(jù)的索引過程,存入主分片 0倚喂。然后并行轉(zhuǎn)發(fā)數(shù)據(jù)給分配有 shard 0 的副本分片的 Node 1 和 Node 2每篷。當收到任一節(jié)點匯報副本分片數(shù)據(jù)寫入成功,Node 3 即返回給初始的接收節(jié)點 Node 1,宣布數(shù)據(jù)寫入成功焦读。Node 1 返回成功響應給客戶端子库。
圖 2-9
這個過程中,有幾個參數(shù)可以用來控制或變更其行為:
- wait_for_active_shards
上面示例中矗晃,2 個副本分片只要有 1 個成功仑嗅,就可以返回給客戶端了。這點也是有配置項的喧兄。其默認值的計算來源如下:
int( (primary + number_of_replicas) / 2 ) + 1
根據(jù)需要,也可以將參數(shù)設置為 one啊楚,表示僅寫完主分片就返回吠冤,等同于 async;還可以設置為 all恭理,表示等所有副本分片都寫完才能返回拯辙。
- timeout
如果集群出現(xiàn)異常,有些分片當前不可用颜价,ES 默認會等待 1 分鐘看分片能否恢復涯保。可以使用?timeout=30s
參數(shù)來縮短這個等待時間周伦。
副本配置和分片配置不一樣夕春,是可以隨時調(diào)整的。有些較大的索引专挪,甚至可以在做 forcemerge 前及志,先把副本全部取消掉,等 optimize 完后寨腔,再重新開啟副本速侈,節(jié)約單個 segment 的重復歸并消耗。
# curl -XPUT http://127.0.0.1:9200/logstash-mweibo-2015.05.02/_settings -d '{ "index": { "number_of_replicas" : 0 }}'
- 1
- 2
- 3
shard 的 allocate 控制
某個 shard 分配在哪個節(jié)點上迫卢,一般來說倚搬,是由 ES 自動決定的。以下幾種情況會觸發(fā)分配動作:
- 新索引生成
- 索引的刪除
- 新增副本分片
- 節(jié)點增減引發(fā)的數(shù)據(jù)均衡
ES 提供了一系列參數(shù)詳細控制這部分邏輯:
- cluster.routing.allocation.enable
該參數(shù)用來控制允許分配哪種分片乾蛤。默認是all
每界。可選項還包括primaries
和new_primaries
家卖。none
則徹底拒絕分片盆犁。該參數(shù)的作用,本書稍后集群升級章節(jié)會有說明篡九。 - cluster.routing.allocation.allow_rebalance
該參數(shù)用來控制什么時候允許數(shù)據(jù)均衡谐岁。默認是indices_all_active
,即要求所有分片都正常啟動成功以后,才可以進行數(shù)據(jù)均衡操作伊佃,否則的話窜司,在集群重啟階段,會浪費太多流量了航揉。 - cluster.routing.allocation.cluster_concurrent_rebalance
該參數(shù)用來控制集群內(nèi)同時運行的數(shù)據(jù)均衡任務個數(shù)塞祈。默認是 2 個。如果有節(jié)點增減帅涂,且集群負載壓力不高的時候议薪,可以適當加大。 - cluster.routing.allocation.node_initial_primaries_recoveries
該參數(shù)用來控制節(jié)點重啟時媳友,允許同時恢復幾個主分片斯议。默認是 4 個。如果節(jié)點是多磁盤醇锚,且 IO 壓力不大哼御,可以適當加大。 - cluster.routing.allocation.node_concurrent_recoveries
該參數(shù)用來控制節(jié)點除了主分片重啟恢復以外其他情況下焊唬,允許同時運行的數(shù)據(jù)恢復任務恋昼。默認是 2 個。所以赶促,節(jié)點重啟時液肌,可以看到主分片迅速恢復完成,副本分片的恢復卻很慢鸥滨。除了副本分片本身數(shù)據(jù)要通過網(wǎng)絡復制以外矩屁,并發(fā)線程本身也減少了一半。當然爵赵,這種設置也是有道理的——主分片一定是本地恢復吝秕,副本分片卻需要走網(wǎng)絡,帶寬是有限的空幻。從 ES 1.6 開始烁峭,冷索引的副本分片可以本地恢復,這個參數(shù)也就是可以適當加大了秕铛。 - indices.recovery.concurrent_streams
該參數(shù)用來控制節(jié)點從網(wǎng)絡復制恢復副本分片時的數(shù)據(jù)流個數(shù)约郁。默認是 3 個〉剑可以配合上一條配置一起加大鬓梅。 - indices.recovery.max_bytes_per_sec
該參數(shù)用來控制節(jié)點恢復時的速率。默認是 40MB谨湘。顯然是比較小的绽快,建議加大芥丧。
此外,ES 還有一些其他的分片分配控制策略坊罢。比如以 tag
和 rack_id
作為區(qū)分等续担。一般來說,Elastic Stack 場景中使用不多活孩。運維人員可能比較常見的策略有兩種:
- 磁盤限額
為了保護節(jié)點數(shù)據(jù)安全物遇,ES 會定時(cluster.info.update.interval
,默認 30 秒)檢查一下各節(jié)點的數(shù)據(jù)目錄磁盤使用情況憾儒。在達到cluster.routing.allocation.disk.watermark.low
(默認 85%)的時候询兴,新索引分片就不會再分配到這個節(jié)點上了。在達到cluster.routing.allocation.disk.watermark.high
(默認 90%)的時候起趾,就會觸發(fā)該節(jié)點現(xiàn)存分片的數(shù)據(jù)均衡诗舰,把數(shù)據(jù)挪到其他節(jié)點上去。這兩個值不但可以寫百分比阳掐,還可以寫具體的字節(jié)數(shù)始衅。有些公司可能出于成本考慮冷蚂,對磁盤使用率有一定的要求缭保,需要適當抬高這個配置:
# curl -XPUT localhost:9200/_cluster/settings -d '{ "transient" : { "cluster.routing.allocation.disk.watermark.low" : "85%", "cluster.routing.allocation.disk.watermark.high" : "10gb", "cluster.info.update.interval" : "1m" }}'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 熱索引分片不均
默認情況下,ES 集群的數(shù)據(jù)均衡策略是以各節(jié)點的分片總數(shù)(indices_all_active)作為基準的蝙茶。這對于搜索服務來說無疑是均衡搜索壓力提高性能的好辦法艺骂。但是對于 Elastic Stack 場景,一般壓力集中在新索引的數(shù)據(jù)寫入方面隆夯。正常運行的時候钳恕,也沒有問題。但是當集群擴容時蹄衷,新加入集群的節(jié)點忧额,分片總數(shù)遠遠低于其他節(jié)點。這時候如果有新索引創(chuàng)建愧口,ES 的默認策略會導致新索引的所有主分片幾乎全分配在這臺新節(jié)點上睦番。整個集群的寫入壓力,壓在一個節(jié)點上耍属,結果很可能是這個節(jié)點直接被壓死托嚣,集群出現(xiàn)異常。
所以厚骗,對于 Elastic Stack 場景示启,強烈建議大家預先計算好索引的分片數(shù)后,配置好單節(jié)點分片的限額领舰。比如夫嗓,一個 5 節(jié)點的集群迟螺,索引主分片 10 個,副本 1 份啤月。則平均下來每個節(jié)點應該有 4 個分片煮仇,那么就配置:
# curl -s -XPUT http://127.0.0.1:9200/logstash-2015.05.08/_settings -d '{ "index": { "routing.allocation.total_shards_per_node" : "5" }}'
- 1
- 2
- 3
注意,這里配置的是 5 而不是 4谎仲。因為我們需要預防有機器故障浙垫,分片發(fā)生遷移的情況。如果寫的是 4郑诺,那么分片遷移會失敗夹姥。
此外,另一種方式則更加玄妙辙诞,Elasticsearch 中有一系列參數(shù)辙售,相互影響,最終聯(lián)合決定分片分配:
- cluster.routing.allocation.balance.shard
節(jié)點上分配分片的權重飞涂,默認為 0.45旦部。數(shù)值越大越傾向于在節(jié)點層面均衡分片。 - cluster.routing.allocation.balance.index
每個索引往單個節(jié)點上分配分片的權重较店,默認為 0.55士八。數(shù)值越大越傾向于在索引層面均衡分片。 - cluster.routing.allocation.balance.threshold
大于閾值則觸發(fā)均衡操作梁呈。默認為1婚度。
Elasticsearch 中的計算方法是:
(indexBalance * (node.numShards(index) – avgShardsPerNode(index)) + shardBalance * (node.numShards() – avgShardsPerNode)) <=> weightthreshold
所以,也可以采取加大 cluster.routing.allocation.balance.index
官卡,甚至設置 cluster.routing.allocation.balance.shard
為 0 來盡量采用索引內(nèi)的節(jié)點均衡蝗茁。
reroute 接口
上面說的各種配置,都是從策略層面寻咒,控制分片分配的選擇哮翘。在必要的時候,還可以通過 ES 的 reroute 接口毛秘,手動完成對分片的分配選擇的控制饭寺。
reroute 接口支持五種指令:allocate_replica
, allocate_stale_primary
, allocate_empty_primary
,move
和 cancel
熔脂。常用的一般是 allocate 和 move:
-
allocate_*
指令
因為負載過高等原因佩研,有時候個別分片可能長期處于 UNASSIGNED 狀態(tài),我們就可以手動分配分片到指定節(jié)點上霞揉。默認情況下只允許手動分配副本分片(即使用 allocate_replica
)旬薯,所以如果要分配主分片,需要單獨加一個 accept_data_loss
選項:
# curl -XPOST 127.0.0.1:9200/_cluster/reroute -d '{ "commands" : [ { "allocate_stale_primary" : { "index" : "logstash-2015.05.27", "shard" : 61, "node" : "10.19.0.77", "accept_data_loss" : true } } ]}'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
注意适秩,allocate_stale_primary
表示準備分配到的節(jié)點上可能有老版本的歷史數(shù)據(jù)绊序,運行時請?zhí)崆按_認一下是哪個節(jié)點上保留有這個分片的實際目錄硕舆,且目錄大小最大。然后手動分配到這個節(jié)點上骤公。以此減少數(shù)據(jù)丟失抚官。
- move 指令
因為負載過高,磁盤利用率過高阶捆,服務器下線凌节,更換磁盤等原因,可以會需要從節(jié)點上移走部分分片:
curl -XPOST 127.0.0.1:9200/_cluster/reroute -d '{ "commands" : [ { "move" : { "index" : "logstash-2015.05.22", "shard" : 0, "from_node" : "10.19.0.81", "to_node" : "10.19.0.104" } } ]}'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
分配失敗原因
如果是自己手工 reroute 失敗洒试,Elasticsearch 返回的響應中會帶上失敗的原因倍奢。不過格式非常難看,一堆 YES垒棋,NO卒煞。從 5.0 版本開始,Elasticsearch 新增了一個 allocation explain 接口叼架,專門用來解釋指定分片的具體失敗理由:
curl -XGET 'http://localhost:9200/_cluster/allocation/explain' -d'{ "index": "logstash-2016.10.31", "shard": 0, "primary": false }'
- 1
- 2
- 3
- 4
- 5
- 6
得到的響應如下:
{ "shard" : { "index" : "myindex", "index_uuid" : "KnW0-zELRs6PK84l0r38ZA", "id" : 0, "primary" : false }, "assigned" : false, "shard_state_fetch_pending": false, "unassigned_info" : { "reason" : "INDEX_CREATED", "at" : "2016-03-22T20:04:23.620Z" }, "allocation_delay_ms" : 0, "remaining_delay_ms" : 0, "nodes" : { "V-Spi0AyRZ6ZvKbaI3691w" : { "node_name" : "H5dfFeA", "node_attributes" : { "bar" : "baz" }, "store" : { "shard_copy" : "NONE" }, "final_decision" : "NO", "final_explanation" : "the shard cannot be assigned because one or more allocation decider returns a 'NO' decision", "weight" : 0.06666675, "decisions" : [ { "decider" : "filter", "decision" : "NO", "explanation" : "node does not match index include filters [foo:\"bar\"]" } ] }, "Qc6VL8c5RWaw1qXZ0Rg57g" : { ...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
這會是很長一串 JSON畔裕,把集群里所有的節(jié)點都列上來,挨個解釋為什么不能分配到這個節(jié)點乖订。
節(jié)點下線
集群中個別節(jié)點出現(xiàn)故障預警等情況扮饶,需要下線,也是 Elasticsearch 運維工作中常見的情況垢粮。如果已經(jīng)穩(wěn)定運行過一段時間的集群贴届,每個節(jié)點上都會保存有數(shù)量不少的分片靠粪。這種時候通過 reroute 接口手動轉(zhuǎn)移蜡吧,就顯得太過麻煩了。這個時候占键,有另一種方式:
curl -XPUT 127.0.0.1:9200/_cluster/settings -d '{ "transient" :{ "cluster.routing.allocation.exclude._ip" : "10.0.0.1" }}'
- 1
- 2
- 3
- 4
- 5
Elasticsearch 集群就會自動把這個 IP 上的所有分片昔善,都自動轉(zhuǎn)移到其他節(jié)點上。等到轉(zhuǎn)移完成畔乙,這個空節(jié)點就可以毫無影響的下線了君仆。
和 _ip
類似的參數(shù)還有 _host
, _name
等。此外牲距,這類參數(shù)不單是 cluster 級別返咱,也可以是 index 級別。下一小節(jié)就是 index 級別的用例牍鞠。
冷熱數(shù)據(jù)的讀寫分離
Elasticsearch 集群一個比較突出的問題是: 用戶做一次大的查詢的時候, 非常大量的讀 IO 以及聚合計算導致機器 Load 升高, CPU 使用率上升, 會影響阻塞到新數(shù)據(jù)的寫入, 這個過程甚至會持續(xù)幾分鐘咖摹。所以,可能需要仿照 MySQL 集群一樣难述,做讀寫分離萤晴。
實施方案
- N 臺機器做熱數(shù)據(jù)的存儲, 上面只放當天的數(shù)據(jù)吐句。這 N 臺熱數(shù)據(jù)節(jié)點上面的 elasticsearc.yml 中配置
node.attr.tag: hot
- 之前的數(shù)據(jù)放在另外的 M 臺機器上。這 M 臺冷數(shù)據(jù)節(jié)點中配置
node.attr.tag: stale
- 模板中控制對新建索引添加 hot 標簽:
{ "order" : 0, "template" : "*", "settings" : { "index.routing.allocation.include.tag" : "hot" }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 每天計劃任務更新索引的配置, 將 tag 更改為 stale, 索引會自動遷移到 M 臺冷數(shù)據(jù)節(jié)點
curl -XPUT http://127.0.0.1:9200/indexname/_settings -d'{ "index": { "routing": { "allocation": { "include": { "tag": "stale" } } } }}'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
這樣店读,寫操作集中在 N 臺熱數(shù)據(jù)節(jié)點上嗦枢,大范圍的讀操作集中在 M 臺冷數(shù)據(jù)節(jié)點上。避免了堵塞影響屯断。
該方案運用的文虏,是 Elasticsearch 中的 allocation filter 功能,詳細說明見:https://www.elastic.co/guide/en/elasticsearch/reference/master/shard-allocation-filtering.html
集群自動發(fā)現(xiàn)
ES 是一個 P2P 類型(使用 gossip 協(xié)議)的分布式系統(tǒng)殖演,除了集群狀態(tài)管理以外择葡,其他所有的請求都可以發(fā)送到集群內(nèi)任意一臺節(jié)點上,這個節(jié)點可以自己找到需要轉(zhuǎn)發(fā)給哪些節(jié)點剃氧,并且直接跟這些節(jié)點通信敏储。
所以,從網(wǎng)絡架構及服務配置上來說朋鞍,構建集群所需要的配置極其簡單已添。在 Elasticsearch 2.0 之前,無阻礙的網(wǎng)絡下滥酥,所有配置了相同 cluster.name
的節(jié)點都自動歸屬到一個集群中更舞。
2.0 版本之后,基于安全的考慮坎吻,Elasticsearch 稍作了調(diào)整缆蝉,避免開發(fā)環(huán)境過于隨便造成的麻煩。
unicast 方式
ES 從 2.0 版本開始瘦真,默認的自動發(fā)現(xiàn)方式改為了單播(unicast)方式刊头。配置里提供幾臺節(jié)點的地址,ES 將其視作 gossip router 角色诸尽,借以完成集群的發(fā)現(xiàn)原杂。由于這只是 ES 內(nèi)一個很小的功能,所以 gossip router 角色并不需要單獨配置您机,每個 ES 節(jié)點都可以擔任穿肄。所以,采用單播方式的集群际看,各節(jié)點都配置相同的幾個節(jié)點列表作為 router 即可咸产。
此外,考慮到節(jié)點有時候因為高負載仲闽,慢 GC 等原因可能會有偶爾沒及時響應 ping 包的可能脑溢,一般建議稍微加大 Fault Detection 的超時時間。
同樣基于安全考慮做的變更還有監(jiān)聽的主機名“遥現(xiàn)在默認只監(jiān)聽本地 lo 網(wǎng)卡上焚志。所以正式環(huán)境上需要修改配置為監(jiān)聽具體的網(wǎng)卡衣迷。
network.host: "192.168.0.2" discovery.zen.minimum_master_nodes: 3discovery.zen.ping_timeout: 100sdiscovery.zen.fd.ping_timeout: 100sdiscovery.zen.ping.unicast.hosts: ["10.19.0.97","10.19.0.98","10.19.0.99","10.19.0.100"]
- 1
- 2
- 3
- 4
- 5
上面的配置中,兩個 timeout 可能會讓人有所迷惑酱酬。這里的 fd 是 fault detection 的縮寫壶谒。也就是說:
- discovery.zen.ping_timeout 參數(shù)僅在加入或者選舉 master 主節(jié)點的時候才起作用;
- discovery.zen.fd.ping_timeout 參數(shù)則在穩(wěn)定運行的集群中膳沽,master 檢測所有節(jié)點汗菜,以及節(jié)點檢測 master 是否暢通時長期有用。
既然是長期有用挑社,自然還有運行間隔和重試的配置陨界,也可以根據(jù)實際情況調(diào)整:
discovery.zen.fd.ping_interval: 10sdiscovery.zen.fd.ping_retries: 10
- 1
- 2
(本文完)
文本整理自《ELKstack權威指南》