Elasticsearch架構(gòu)原理

歡迎訪問我的博客查看原文:http://wangnan.tech

注:文本整理自《ELKstack權(quán)威指南》

架構(gòu)原理

本書作為 Elastic Stack 指南赞草,關(guān)注于 Elasticsearch 在日志和數(shù)據(jù)分析場景的應(yīng)用,并不打算對底層的 Lucene 原理或者 Java 編程做詳細(xì)的介紹桃漾,但是 Elasticsearch 層面上的一些架構(gòu)設(shè)計贷盲,對我們做性能調(diào)優(yōu)淘这,故障處理剥扣,具有非常重要的影響巩剖。

所以,作為 ES 部分的起始章節(jié)钠怯,先從數(shù)據(jù)流向和分布的層面佳魔,介紹一下 ES 的工作原理,以及相關(guān)的可控項晦炊。各位讀者可以跳過這節(jié)先行閱讀后面的運(yùn)維操作部分鞠鲜,但作為性能調(diào)優(yōu)的基礎(chǔ)知識宁脊,依然建議大家抽時間返回來了解。

帶著問題學(xué)習(xí)

  1. 寫入的數(shù)據(jù)是如何變成elasticsearch里可以被檢索和聚合的索引內(nèi)容的贤姆?
  2. lucene如何實(shí)現(xiàn)準(zhǔn)實(shí)時索引榆苞?
  3. 什么是segment?
  4. 什么是commit霞捡?
  5. segment的數(shù)據(jù)來自哪里坐漏?
  6. segment在寫入磁盤前就可以被檢索,是因?yàn)槔昧耸裁矗?/li>
  7. elasticsearch中的refresh操作是什么碧信?配置項是哪個?設(shè)置的命令是什么?
  8. refresh只是寫到了文件系統(tǒng)緩存祸轮,那么實(shí)際寫入磁盤是由什么控制呢芳室?,如果這期間發(fā)生錯誤和故障呈枉,數(shù)據(jù)會不會丟失趁尼?
  9. 什么是translog日志?什么時候會被清空碴卧?什么是flush操作弱卡?配置項是什么?怎么配置住册?
    10.什么是段合并婶博?為什么要段合并?段合并線程配置項荧飞?段合并策略凡人?怎么forcemerge(optimize)?
  10. routing的規(guī)則是什么樣的叹阔?replica讀寫過程挠轴?wait_for_active_shards參數(shù)timeout參數(shù) ?
  11. reroute 接口耳幢?
  12. 兩種 自動發(fā)現(xiàn)方式岸晦?

目錄

segment、buffer和translog對實(shí)時性的影響

既然介紹數(shù)據(jù)流向睛藻,首先第一步就是:寫入的數(shù)據(jù)是如何變成 Elasticsearch 里可以被檢索和聚合的索引內(nèi)容的启上?

以單文件的靜態(tài)層面看,每個全文索引都是一個詞元的倒排索引店印,具體涉及到全文索引的通用知識冈在,這里不單獨(dú)介紹,有興趣的讀者可以閱讀《Lucene in Action》等書籍詳細(xì)了解按摘。

動態(tài)更新的 Lucene 索引

以在線動態(tài)服務(wù)的層面看包券,要做到實(shí)時更新條件下數(shù)據(jù)的可用和可靠纫谅,就需要在倒排索引的基礎(chǔ)上,再做一系列更高級的處理溅固。

其實(shí)總結(jié)一下 Lucene 的處理辦法付秕,很簡單,就是一句話:新收到的數(shù)據(jù)寫到新的索引文件里侍郭。

Lucene 把每次生成的倒排索引盹牧,叫做一個段(segment)。然后另外使用一個 commit 文件励幼,記錄索引內(nèi)所有的 segment汰寓。而生成 segment 的數(shù)據(jù)來源,則是內(nèi)存中的 buffer苹粟。也就是說有滑,動態(tài)更新過程如下:

  1. 當(dāng)前索引有 3 個 segment 可用。索引狀態(tài)如圖 2-1嵌削;


    A Lucene index with a commit point and three segments

    圖 2-1

  2. 新接收的數(shù)據(jù)進(jìn)入內(nèi)存 buffer毛好。索引狀態(tài)如圖 2-2;


    A Lucene index with new documents in the in-memory buffer, ready to commit

    圖 2-2

  3. 內(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

利用磁盤緩存實(shí)現(xiàn)的準(zhǔn)實(shí)時檢索

既然涉及到磁盤吼驶,那么一個不可避免的問題就來了:磁盤太慢了!對我們要求實(shí)時性很高的服務(wù)來說店煞,這種處理還不夠蟹演。所以,在第 3 步的處理中顷蟀,還有一個中間狀態(tài):

  1. 內(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

  2. 文件系統(tǒng)緩存真正同步到磁盤上,commit 文件更新囤萤。達(dá)到圖 2-3 中的狀態(tài)昼窗。

這一步刷到文件系統(tǒng)緩存的步驟,在 Elasticsearch 中阁将,是默認(rèn)設(shè)置為 1 秒間隔的膏秫,對于大多數(shù)應(yīng)用來說右遭,幾乎就相當(dāng)于是實(shí)時可搜索了做盅。Elasticsearch 也提供了單獨(dú)的 /_refresh 接口缤削,用戶如果對 1 秒間隔還不滿意的,可以主動調(diào)用該接口來保證搜索可見吹榴。

注:5.0 中還提供了一個新的請求參數(shù):?refresh=wait_for亭敢,可以在寫入數(shù)據(jù)后不強(qiáng)制刷新但一直等到刷新才返回。

不過對于 Elastic Stack 的日志場景來說图筹,恰恰相反帅刀,我們并不需要如此高的實(shí)時性,而是需要更快的寫入性能远剩。所以扣溺,一般來說,我們反而會通過 /_settings 接口或者定制 template 的方式瓜晤,加大 refresh_interval 參數(shù):

# curl -XPOST http://127.0.0.1:9200/logstash-2015.06.21/_settings -d'
{ "refresh_interval": "10s" }
'

如果是導(dǎo)入歷史數(shù)據(jù)的場合锥余,那甚至可以先完全關(guān)閉掉:

# curl -XPUT http://127.0.0.1:9200/logstash-2015.05.01 -d'
{
  "settings" : {
    "refresh_interval": "-1"
  }
}'

在導(dǎo)入完成以后,修改回來或者手動調(diào)用一次即可:

# curl -XPOST http://127.0.0.1:9200/logstash-2015.05.01/_refresh

translog 提供的磁盤同步控制

既然 refresh 只是寫到文件系統(tǒng)緩存痢掠,那么第 4 步寫到實(shí)際磁盤又是有什么來控制的驱犹?如果這期間發(fā)生主機(jī)錯誤、硬件故障等異常情況足画,數(shù)據(jù)會不會丟失雄驹?

這里,其實(shí)有另一個機(jī)制來控制淹辞。Elasticsearch 在把數(shù)據(jù)寫入到內(nèi)存 buffer 的同時医舆,其實(shí)還另外記錄了一個 translog 日志。也就是說象缀,第 2 步并不是圖 2-2 的狀態(tài)彬向,而是像圖 2-5 這樣:

New documents are added to the in-memory buffer and appended to the transaction log

圖 2-5

在第 3 和第 4 步,refresh 發(fā)生的時候攻冷,translog 日志文件依然保持原樣娃胆,如圖 2-6:

The transaction log keeps accumulating documents

圖 2-6

也就是說,如果在這期間發(fā)生異常等曼,Elasticsearch 會從 commit 位置開始里烦,恢復(fù)整個 translog 文件中的記錄,保證數(shù)據(jù)一致性禁谦。

等到真正把 segment 刷到磁盤胁黑,且 commit 文件進(jìn)行更新的時候, translog 文件才清空州泊。這一步丧蘸,叫做 flush。同樣遥皂,Elasticsearch 也提供了 /_flush 接口力喷。

對于 flush 操作刽漂,Elasticsearch 默認(rèn)設(shè)置為:每 30 分鐘主動進(jìn)行一次 flush,或者當(dāng) translog 文件大小大于 512MB (老版本是 200MB)時弟孟,主動進(jìn)行一次 flush贝咙。這兩個行為,可以分別通過 index.translog.flush_threshold_periodindex.translog.flush_threshold_size 參數(shù)修改拂募。

如果對這兩種控制方式都不滿意庭猩,Elasticsearch 還可以通過 index.translog.flush_threshold_ops 參數(shù),控制每收到多少條數(shù)據(jù)后 flush 一次陈症。

translog 的一致性

索引數(shù)據(jù)的一致性通過 translog 保證蔼水。那么 translog 文件自己呢?

默認(rèn)情況下录肯,Elasticsearch 每 5 秒徙缴,或每次請求操作結(jié)束前,會強(qiáng)制刷新 translog 日志到磁盤上嘁信。

后者是 Elasticsearch 2.0 新加入的特性于样。為了保證不丟數(shù)據(jù),每次 index潘靖、bulk穿剖、delete、update 完成的時候卦溢,一定觸發(fā)刷新 translog 到磁盤上糊余,才給請求返回 200 OK。這個改變在提高數(shù)據(jù)安全性的同時當(dāng)然也降低了一點(diǎn)性能单寂。

如果你不在意這點(diǎn)可能性贬芥,還是希望性能優(yōu)先,可以在 index template 里設(shè)置如下參數(shù):

{
    "index.translog.durability": "async"
}

Elasticsearch 分布式索引

大家可能注意到了宣决,前面一段內(nèi)容蘸劈,一直寫的是"Lucene 索引"。這個區(qū)別在于尊沸,Elasticsearch 為了完成分布式系統(tǒng)威沫,對一些名詞概念作了變動。索引成為了整個集群級別的命名洼专,而在單個主機(jī)上的Lucene 索引棒掠,則被命名為分片(shard)。至于數(shù)據(jù)是怎么識別到自己應(yīng)該在哪個分片屁商,請閱讀稍后有關(guān) routing 的章節(jié)烟很。

segment merge對寫入性能的影響

通過上節(jié)內(nèi)容,我們知道了數(shù)據(jù)怎么進(jìn)入 ES 并且如何才能讓數(shù)據(jù)更快的被檢索使用。其中用一句話概括了 Lucene 的設(shè)計思路就是"開新文件"雾袱。從另一個方面看恤筛,開新文件也會給服務(wù)器帶來負(fù)載壓力。因?yàn)槟J(rèn)每 1 秒谜酒,都會有一個新文件產(chǎn)生,每個文件都需要有文件句柄妻枕,內(nèi)存僻族,CPU 使用等各種資源。一天有 86400 秒屡谐,設(shè)想一下述么,每次請求要掃描一遍 86400 個文件,這個響應(yīng)性能絕對好不了愕掏!

為了解決這個問題度秘,ES 會不斷在后臺運(yùn)行任務(wù),主動將這些零散的 segment 做數(shù)據(jù)歸并饵撑,盡量讓索引內(nèi)只保有少量的剑梳,每個都比較大的,segment 文件滑潘。這個過程是有獨(dú)立的線程來進(jìn)行的垢乙,并不影響新 segment 的產(chǎn)生。歸并過程中语卤,索引狀態(tài)如圖 2-7追逮,尚未完成的較大的 segment 是被排除在檢索可見范圍之外的:

Two commited segments and one uncommited segment in the process of being merged into a bigger segment

圖 2-7

當(dāng)歸并完成,較大的這個 segment 刷到磁盤后粹舵,commit 文件做出相應(yīng)變更钮孵,刪除之前幾個小 segment,改成新的大 segment眼滤。等檢索請求都從小 segment 轉(zhuǎn)到大 segment 上以后巴席,刪除沒用的小 segment。這時候诅需,索引里 segment 數(shù)量就下降了情妖,狀態(tài)如圖 2-8 所示:

Once merging has finished, the old segments are deleted

圖 2-8

歸并線程配置

segment 歸并的過程,需要先讀取 segment诱担,歸并計算毡证,再寫一遍 segment,最后還要保證刷到磁盤蔫仙×暇Γ可以說,這是一個非常消耗磁盤 IO 和 CPU 的任務(wù)。所以恤煞,ES 提供了對歸并線程的限速機(jī)制屎勘,確保這個任務(wù)不會過分影響到其他任務(wù)。

在 5.0 之前居扒,歸并線程的限速配置 indices.store.throttle.max_bytes_per_sec 是 20MB概漱。對于寫入量較大,磁盤轉(zhuǎn)速較高喜喂,甚至使用 SSD 盤的服務(wù)器來說瓤摧,這個限速是明顯過低的。對于 Elastic Stack 應(yīng)用玉吁,社區(qū)廣泛的建議是可以適當(dāng)調(diào)大到 100MB或者更高照弥。

# curl -XPUT http://127.0.0.1:9200/_cluster/settings -d'
{
    "persistent" : {
        "indices.store.throttle.max_bytes_per_sec" : "100mb"
    }
}'

5.0 開始,ES 對此作了大幅度改進(jìn)进副,使用了 Lucene 的 CMS(ConcurrentMergeScheduler) 的 auto throttle 機(jī)制这揣,正常情況下已經(jīng)不再需要手動配置 indices.store.throttle.max_bytes_per_sec 了。官方文檔中都已經(jīng)刪除了相關(guān)介紹影斑,不過從源碼中還是可以看到给赞,這個值目前的默認(rèn)設(shè)置是 10240 MB。

歸并線程的數(shù)目矫户,ES 也是有所控制的塞俱。默認(rèn)數(shù)目的計算公式是: Math.min(3, Runtime.getRuntime().availableProcessors() / 2)。即服務(wù)器 CPU 核數(shù)的一半大于 3 時吏垮,啟動 3 個歸并線程障涯;否則啟動跟 CPU 核數(shù)的一半相等的線程數(shù)。相信一般做 Elastic Stack 的服務(wù)器 CPU 合數(shù)都會在 6 個以上膳汪。所以一般來說就是 3 個歸并線程唯蝶。如果你確定自己磁盤性能跟不上,可以降低 index.merge.scheduler.max_thread_count 配置遗嗽,免得 IO 情況更加惡化粘我。

歸并策略

歸并線程是按照一定的運(yùn)行策略來挑選 segment 進(jìn)行歸并的。主要有以下幾條:

  • index.merge.policy.floor_segment
    默認(rèn) 2MB痹换,小于這個大小的 segment征字,優(yōu)先被歸并。
  • index.merge.policy.max_merge_at_once
    默認(rèn)一次最多歸并 10 個 segment
  • index.merge.policy.max_merge_at_once_explicit
    默認(rèn) forcemerge 時一次最多歸并 30 個 segment娇豫。
  • index.merge.policy.max_merged_segment
    默認(rèn) 5 GB匙姜,大于這個大小的 segment,不用參與歸并冯痢。forcemerge 除外氮昧。

根據(jù)這段策略框杜,其實(shí)我們也可以從另一個角度考慮如何減少 segment 歸并的消耗以及提高響應(yīng)的辦法:加大 flush 間隔,盡量讓每次新生成的 segment 本身大小就比較大袖肥。

forcemerge 接口

既然默認(rèn)的最大 segment 大小是 5GB咪辱。那么一個比較龐大的數(shù)據(jù)索引,就必然會有為數(shù)不少的 segment 永遠(yuǎn)存在椎组,這對文件句柄油狂,內(nèi)存等資源都是極大的浪費(fèi)。但是由于歸并任務(wù)太消耗資源寸癌,所以一般不太選擇加大 index.merge.policy.max_merged_segment 配置专筷,而是在負(fù)載較低的時間段,通過 forcemerge 接口灵份,強(qiáng)制歸并 segment仁堪。

# curl -XPOST http://127.0.0.1:9200/logstash-2015-06.10/_forcemerge?max_num_segments=1

由于 forcemerge 線程對資源的消耗比普通的歸并線程大得多哮洽,所以填渠,絕對不建議對還在寫入數(shù)據(jù)的熱索引執(zhí)行這個操作。這個問題對于 Elastic Stack 來說非常好辦鸟辅,一般索引都是按天分割的氛什。更合適的任務(wù)定義方式,請閱讀本書稍后的 curator 章節(jié)匪凉。

routing和replica的讀寫過程

之前兩節(jié)枪眉,完整介紹了在單個 Lucene 索引,即 ES 分片內(nèi)的數(shù)據(jù)寫入流程≡俨悖現(xiàn)在徹底回到 ES 的分布式層面上來贸铜,當(dāng)一個 ES 節(jié)點(diǎn)收到一條數(shù)據(jù)的寫入請求時,它是如何確認(rèn)這個數(shù)據(jù)應(yīng)該存儲在哪個節(jié)點(diǎn)的哪個分片上的聂受?

路由計算

作為一個沒有額外依賴的簡單的分布式方案蒿秦,ES 在這個問題上同樣選擇了一個非常簡潔的處理方式,對任一條數(shù)據(jù)計算其對應(yīng)分片的方式如下:

shard = hash(routing) % number_of_primary_shards

每個數(shù)據(jù)都有一個 routing 參數(shù)蛋济,默認(rèn)情況下棍鳖,就使用其 _id 值。將其 _id 值計算哈希后碗旅,對索引的主分片數(shù)取余渡处,就是數(shù)據(jù)實(shí)際應(yīng)該存儲到的分片 ID。

由于取余這個計算祟辟,完全依賴于分母医瘫,所以導(dǎo)致 ES 索引有一個限制,索引的主分片數(shù)旧困,不可以隨意修改登下。因?yàn)橐坏┲鞣制瑪?shù)不一樣茫孔,所以數(shù)據(jù)的存儲位置計算結(jié)果都會發(fā)生改變,索引數(shù)據(jù)就完全不可讀了被芳。

副本一致性

作為分布式系統(tǒng)缰贝,數(shù)據(jù)副本可算是一個標(biāo)配。ES 數(shù)據(jù)寫入流程畔濒,自然也涉及到副本剩晴。在有副本配置的情況下,數(shù)據(jù)從發(fā)向 ES 節(jié)點(diǎn)侵状,到接到 ES 節(jié)點(diǎn)響應(yīng)返回赞弥,流向如下(附圖 2-9):

  1. 客戶端請求發(fā)送給 Node 1 節(jié)點(diǎn),注意圖中 Node 1 是 Master 節(jié)點(diǎn)趣兄,實(shí)際完全可以不是绽左。
  2. Node 1 用數(shù)據(jù)的 _id 取余計算得到應(yīng)該講數(shù)據(jù)存儲到 shard 0 上。通過 cluster state 信息發(fā)現(xiàn) shard 0 的主分片已經(jīng)分配到了 Node 3 上艇潭。Node 1 轉(zhuǎn)發(fā)請求數(shù)據(jù)給 Node 3拼窥。
  3. Node 3 完成請求數(shù)據(jù)的索引過程,存入主分片 0蹋凝。然后并行轉(zhuǎn)發(fā)數(shù)據(jù)給分配有 shard 0 的副本分片的 Node 1 和 Node 2鲁纠。當(dāng)收到任一節(jié)點(diǎn)匯報副本分片數(shù)據(jù)寫入成功,Node 3 即返回給初始的接收節(jié)點(diǎn) Node 1鳍寂,宣布數(shù)據(jù)寫入成功改含。Node 1 返回成功響應(yīng)給客戶端。
Creating, indexing or deleting a single document

圖 2-9

這個過程中迄汛,有幾個參數(shù)可以用來控制或變更其行為:

  • wait_for_active_shards
    上面示例中捍壤,2 個副本分片只要有 1 個成功,就可以返回給客戶端了鞍爱。這點(diǎn)也是有配置項的鹃觉。其默認(rèn)值的計算來源如下:

int( (primary + number_of_replicas) / 2 ) + 1

根據(jù)需要,也可以將參數(shù)設(shè)置為 one硬霍,表示僅寫完主分片就返回帜慢,等同于 async;還可以設(shè)置為 all唯卖,表示等所有副本分片都寫完才能返回粱玲。

  • timeout
    如果集群出現(xiàn)異常,有些分片當(dāng)前不可用拜轨,ES 默認(rèn)會等待 1 分鐘看分片能否恢復(fù)抽减。可以使用 ?timeout=30s 參數(shù)來縮短這個等待時間橄碾。

副本配置和分片配置不一樣卵沉,是可以隨時調(diào)整的颠锉。有些較大的索引,甚至可以在做 forcemerge 前史汗,先把副本全部取消掉琼掠,等 optimize 完后,再重新開啟副本停撞,節(jié)約單個 segment 的重復(fù)歸并消耗瓷蛙。

# curl -XPUT http://127.0.0.1:9200/logstash-mweibo-2015.05.02/_settings -d '{
    "index": { "number_of_replicas" : 0 }
}'

shard 的 allocate 控制

某個 shard 分配在哪個節(jié)點(diǎn)上,一般來說戈毒,是由 ES 自動決定的艰猬。以下幾種情況會觸發(fā)分配動作:

  1. 新索引生成
  2. 索引的刪除
  3. 新增副本分片
  4. 節(jié)點(diǎn)增減引發(fā)的數(shù)據(jù)均衡

ES 提供了一系列參數(shù)詳細(xì)控制這部分邏輯:

  • cluster.routing.allocation.enable
    該參數(shù)用來控制允許分配哪種分片。默認(rèn)是 all埋市」谔遥可選項還包括 primariesnew_primariesnone 則徹底拒絕分片道宅。該參數(shù)的作用食听,本書稍后集群升級章節(jié)會有說明。
  • cluster.routing.allocation.allow_rebalance
    該參數(shù)用來控制什么時候允許數(shù)據(jù)均衡培己。默認(rèn)是 indices_all_active碳蛋,即要求所有分片都正常啟動成功以后胚泌,才可以進(jìn)行數(shù)據(jù)均衡操作省咨,否則的話,在集群重啟階段玷室,會浪費(fèi)太多流量了零蓉。
  • cluster.routing.allocation.cluster_concurrent_rebalance
    該參數(shù)用來控制集群內(nèi)同時運(yùn)行的數(shù)據(jù)均衡任務(wù)個數(shù)。默認(rèn)是 2 個穷缤。如果有節(jié)點(diǎn)增減敌蜂,且集群負(fù)載壓力不高的時候,可以適當(dāng)加大津肛。
  • cluster.routing.allocation.node_initial_primaries_recoveries
    該參數(shù)用來控制節(jié)點(diǎn)重啟時章喉,允許同時恢復(fù)幾個主分片。默認(rèn)是 4 個身坐。如果節(jié)點(diǎn)是多磁盤秸脱,且 IO 壓力不大,可以適當(dāng)加大部蛇。
  • cluster.routing.allocation.node_concurrent_recoveries
    該參數(shù)用來控制節(jié)點(diǎn)除了主分片重啟恢復(fù)以外其他情況下摊唇,允許同時運(yùn)行的數(shù)據(jù)恢復(fù)任務(wù)。默認(rèn)是 2 個涯鲁。所以巷查,節(jié)點(diǎn)重啟時有序,可以看到主分片迅速恢復(fù)完成,副本分片的恢復(fù)卻很慢岛请。除了副本分片本身數(shù)據(jù)要通過網(wǎng)絡(luò)復(fù)制以外旭寿,并發(fā)線程本身也減少了一半。當(dāng)然崇败,這種設(shè)置也是有道理的——主分片一定是本地恢復(fù)许师,副本分片卻需要走網(wǎng)絡(luò),帶寬是有限的僚匆。從 ES 1.6 開始微渠,冷索引的副本分片可以本地恢復(fù),這個參數(shù)也就是可以適當(dāng)加大了咧擂。
  • indices.recovery.concurrent_streams
    該參數(shù)用來控制節(jié)點(diǎn)從網(wǎng)絡(luò)復(fù)制恢復(fù)副本分片時的數(shù)據(jù)流個數(shù)逞盆。默認(rèn)是 3 個∷缮辏可以配合上一條配置一起加大云芦。
  • indices.recovery.max_bytes_per_sec
    該參數(shù)用來控制節(jié)點(diǎn)恢復(fù)時的速率。默認(rèn)是 40MB贸桶。顯然是比較小的舅逸,建議加大。

此外皇筛,ES 還有一些其他的分片分配控制策略琉历。比如以 tagrack_id 作為區(qū)分等。一般來說水醋,Elastic Stack 場景中使用不多旗笔。運(yùn)維人員可能比較常見的策略有兩種:

  1. 磁盤限額
    為了保護(hù)節(jié)點(diǎn)數(shù)據(jù)安全,ES 會定時(cluster.info.update.interval拄踪,默認(rèn) 30 秒)檢查一下各節(jié)點(diǎn)的數(shù)據(jù)目錄磁盤使用情況蝇恶。在達(dá)到 cluster.routing.allocation.disk.watermark.low (默認(rèn) 85%)的時候,新索引分片就不會再分配到這個節(jié)點(diǎn)上了惶桐。在達(dá)到 cluster.routing.allocation.disk.watermark.high (默認(rèn) 90%)的時候撮弧,就會觸發(fā)該節(jié)點(diǎn)現(xiàn)存分片的數(shù)據(jù)均衡,把數(shù)據(jù)挪到其他節(jié)點(diǎn)上去姚糊。這兩個值不但可以寫百分比贿衍,還可以寫具體的字節(jié)數(shù)。有些公司可能出于成本考慮叛拷,對磁盤使用率有一定的要求舌厨,需要適當(dāng)抬高這個配置:
# 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. 熱索引分片不均
    默認(rèn)情況下,ES 集群的數(shù)據(jù)均衡策略是以各節(jié)點(diǎn)的分片總數(shù)(indices_all_active)作為基準(zhǔn)的忿薇。這對于搜索服務(wù)來說無疑是均衡搜索壓力提高性能的好辦法裙椭。但是對于 Elastic Stack 場景躏哩,一般壓力集中在新索引的數(shù)據(jù)寫入方面。正常運(yùn)行的時候揉燃,也沒有問題扫尺。但是當(dāng)集群擴(kuò)容時,新加入集群的節(jié)點(diǎn)炊汤,分片總數(shù)遠(yuǎn)遠(yuǎn)低于其他節(jié)點(diǎn)正驻。這時候如果有新索引創(chuàng)建,ES 的默認(rèn)策略會導(dǎo)致新索引的所有主分片幾乎全分配在這臺新節(jié)點(diǎn)上抢腐。整個集群的寫入壓力姑曙,壓在一個節(jié)點(diǎn)上,結(jié)果很可能是這個節(jié)點(diǎn)直接被壓死迈倍,集群出現(xiàn)異常伤靠。
    所以,對于 Elastic Stack 場景啼染,強(qiáng)烈建議大家預(yù)先計算好索引的分片數(shù)后宴合,配置好單節(jié)點(diǎn)分片的限額。比如迹鹅,一個 5 節(jié)點(diǎn)的集群卦洽,索引主分片 10 個,副本 1 份斜棚。則平均下來每個節(jié)點(diǎn)應(yīng)該有 4 個分片阀蒂,那么就配置:
# curl -s -XPUT http://127.0.0.1:9200/logstash-2015.05.08/_settings -d '{
    "index": { "routing.allocation.total_shards_per_node" : "5" }
}'

注意,這里配置的是 5 而不是 4打肝。因?yàn)槲覀冃枰A(yù)防有機(jī)器故障脂新,分片發(fā)生遷移的情況挪捕。如果寫的是 4粗梭,那么分片遷移會失敗。

此外级零,另一種方式則更加玄妙断医,Elasticsearch 中有一系列參數(shù),相互影響奏纪,最終聯(lián)合決定分片分配:

  • cluster.routing.allocation.balance.shard
    節(jié)點(diǎn)上分配分片的權(quán)重鉴嗤,默認(rèn)為 0.45。數(shù)值越大越傾向于在節(jié)點(diǎn)層面均衡分片序调。
  • cluster.routing.allocation.balance.index
    每個索引往單個節(jié)點(diǎn)上分配分片的權(quán)重醉锅,默認(rèn)為 0.55。數(shù)值越大越傾向于在索引層面均衡分片发绢。
  • cluster.routing.allocation.balance.threshold
    大于閾值則觸發(fā)均衡操作硬耍。默認(rèn)為1垄琐。

Elasticsearch 中的計算方法是:

(indexBalance * (node.numShards(index) – avgShardsPerNode(index)) + shardBalance * (node.numShards() – avgShardsPerNode)) <=> weightthreshold

所以膛腐,也可以采取加大 cluster.routing.allocation.balance.index验靡,甚至設(shè)置 cluster.routing.allocation.balance.shard 為 0 來盡量采用索引內(nèi)的節(jié)點(diǎn)均衡慧库。

reroute 接口

上面說的各種配置尘奏,都是從策略層面商膊,控制分片分配的選擇碗降。在必要的時候矛双,還可以通過 ES 的 reroute 接口汪拥,手動完成對分片的分配選擇的控制牛哺。

reroute 接口支持五種指令:allocate_replica, allocate_stale_primary, allocate_empty_primary陋气,movecancel。常用的一般是 allocate 和 move:

  • allocate_* 指令

因?yàn)樨?fù)載過高等原因引润,有時候個別分片可能長期處于 UNASSIGNED 狀態(tài)恩伺,我們就可以手動分配分片到指定節(jié)點(diǎn)上。默認(rèn)情況下只允許手動分配副本分片(即使用 allocate_replica)椰拒,所以如果要分配主分片晶渠,需要單獨(dú)加一個 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
            }
        }
  ]
}'

注意,allocate_stale_primary 表示準(zhǔn)備分配到的節(jié)點(diǎn)上可能有老版本的歷史數(shù)據(jù)燃观,運(yùn)行時請?zhí)崆按_認(rèn)一下是哪個節(jié)點(diǎn)上保留有這個分片的實(shí)際目錄褒脯,且目錄大小最大。然后手動分配到這個節(jié)點(diǎn)上缆毁。以此減少數(shù)據(jù)丟失番川。

  • move 指令

因?yàn)樨?fù)載過高,磁盤利用率過高脊框,服務(wù)器下線颁督,更換磁盤等原因,可以會需要從節(jié)點(diǎn)上移走部分分片:

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"
            }
        }
  ]
}'

分配失敗原因

如果是自己手工 reroute 失敗浇雹,Elasticsearch 返回的響應(yīng)中會帶上失敗的原因沉御。不過格式非常難看,一堆 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

}'

得到的響應(yīng)如下:

{
    "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" : {
            ...

這會是很長一串 JSON试疙,把集群里所有的節(jié)點(diǎn)都列上來,挨個解釋為什么不能分配到這個節(jié)點(diǎn)抠蚣。

節(jié)點(diǎn)下線

集群中個別節(jié)點(diǎn)出現(xiàn)故障預(yù)警等情況祝旷,需要下線,也是 Elasticsearch 運(yùn)維工作中常見的情況。如果已經(jīng)穩(wěn)定運(yùn)行過一段時間的集群怀跛,每個節(jié)點(diǎn)上都會保存有數(shù)量不少的分片奇昙。這種時候通過 reroute 接口手動轉(zhuǎn)移,就顯得太過麻煩了敌完。這個時候储耐,有另一種方式:

curl -XPUT 127.0.0.1:9200/_cluster/settings -d '{
  "transient" :{
      "cluster.routing.allocation.exclude._ip" : "10.0.0.1"
   }
}'

Elasticsearch 集群就會自動把這個 IP 上的所有分片,都自動轉(zhuǎn)移到其他節(jié)點(diǎn)上滨溉。等到轉(zhuǎn)移完成什湘,這個空節(jié)點(diǎn)就可以毫無影響的下線了。

_ip 類似的參數(shù)還有 _host, _name 等晦攒。此外闽撤,這類參數(shù)不單是 cluster 級別,也可以是 index 級別脯颜。下一小節(jié)就是 index 級別的用例哟旗。

冷熱數(shù)據(jù)的讀寫分離

Elasticsearch 集群一個比較突出的問題是: 用戶做一次大的查詢的時候, 非常大量的讀 IO 以及聚合計算導(dǎo)致機(jī)器 Load 升高, CPU 使用率上升, 會影響阻塞到新數(shù)據(jù)的寫入, 這個過程甚至?xí)掷m(xù)幾分鐘。所以栋操,可能需要仿照 MySQL 集群一樣闸餐,做讀寫分離。

實(shí)施方案

  1. N 臺機(jī)器做熱數(shù)據(jù)的存儲, 上面只放當(dāng)天的數(shù)據(jù)矾芙。這 N 臺熱數(shù)據(jù)節(jié)點(diǎn)上面的 elasticsearc.yml 中配置 node.attr.tag: hot
  2. 之前的數(shù)據(jù)放在另外的 M 臺機(jī)器上舍沙。這 M 臺冷數(shù)據(jù)節(jié)點(diǎn)中配置 node.attr.tag: stale
  3. 模板中控制對新建索引添加 hot 標(biāo)簽:
{
    "order" : 0,
    "template" : "*",
    "settings" : {
      "index.routing.allocation.include.tag" : "hot"
    }
}
  1. 每天計劃任務(wù)更新索引的配置, 將 tag 更改為 stale, 索引會自動遷移到 M 臺冷數(shù)據(jù)節(jié)點(diǎn)
curl -XPUT http://127.0.0.1:9200/indexname/_settings -d'
{
   "index": {
      "routing": {
         "allocation": {
            "include": {
               "tag": "stale"
            }
         }
     }
   }
}'

這樣,寫操作集中在 N 臺熱數(shù)據(jù)節(jié)點(diǎn)上剔宪,大范圍的讀操作集中在 M 臺冷數(shù)據(jù)節(jié)點(diǎn)上拂铡。避免了堵塞影響。

該方案運(yùn)用的葱绒,是 Elasticsearch 中的 allocation filter 功能感帅,詳細(xì)說明見: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é)點(diǎn)上失球,這個節(jié)點(diǎn)可以自己找到需要轉(zhuǎn)發(fā)給哪些節(jié)點(diǎn),并且直接跟這些節(jié)點(diǎn)通信骚秦。

所以她倘,從網(wǎng)絡(luò)架構(gòu)及服務(wù)配置上來說,構(gòu)建集群所需要的配置極其簡單作箍。在 Elasticsearch 2.0 之前,無阻礙的網(wǎng)絡(luò)下前硫,所有配置了相同 cluster.name 的節(jié)點(diǎn)都自動歸屬到一個集群中胞得。

2.0 版本之后,基于安全的考慮屹电,Elasticsearch 稍作了調(diào)整阶剑,避免開發(fā)環(huán)境過于隨便造成的麻煩跃巡。

unicast 方式

ES 從 2.0 版本開始,默認(rèn)的自動發(fā)現(xiàn)方式改為了單播(unicast)方式牧愁。配置里提供幾臺節(jié)點(diǎn)的地址素邪,ES 將其視作 gossip router 角色,借以完成集群的發(fā)現(xiàn)猪半。由于這只是 ES 內(nèi)一個很小的功能兔朦,所以 gossip router 角色并不需要單獨(dú)配置,每個 ES 節(jié)點(diǎn)都可以擔(dān)任磨确。所以沽甥,采用單播方式的集群,各節(jié)點(diǎn)都配置相同的幾個節(jié)點(diǎn)列表作為 router 即可乏奥。

此外摆舟,考慮到節(jié)點(diǎn)有時候因?yàn)楦哓?fù)載,慢 GC 等原因可能會有偶爾沒及時響應(yīng) ping 包的可能邓了,一般建議稍微加大 Fault Detection 的超時時間恨诱。

同樣基于安全考慮做的變更還有監(jiān)聽的主機(jī)名。現(xiàn)在默認(rèn)只監(jiān)聽本地 lo 網(wǎng)卡上骗炉。所以正式環(huán)境上需要修改配置為監(jiān)聽具體的網(wǎng)卡胡野。

network.host: "192.168.0.2" 
discovery.zen.minimum_master_nodes: 3
discovery.zen.ping_timeout: 100s
discovery.zen.fd.ping_timeout: 100s
discovery.zen.ping.unicast.hosts: ["10.19.0.97","10.19.0.98","10.19.0.99","10.19.0.100"]

上面的配置中,兩個 timeout 可能會讓人有所迷惑痕鳍。這里的 fd 是 fault detection 的縮寫硫豆。也就是說:

  • discovery.zen.ping_timeout 參數(shù)僅在加入或者選舉 master 主節(jié)點(diǎn)的時候才起作用;
  • discovery.zen.fd.ping_timeout 參數(shù)則在穩(wěn)定運(yùn)行的集群中笼呆,master 檢測所有節(jié)點(diǎn)熊响,以及節(jié)點(diǎn)檢測 master 是否暢通時長期有用。

既然是長期有用诗赌,自然還有運(yùn)行間隔和重試的配置汗茄,也可以根據(jù)實(shí)際情況調(diào)整:

discovery.zen.fd.ping_interval: 10s
discovery.zen.fd.ping_retries: 10

(本文完)

文本整理自《ELKstack權(quán)威指南》

歡迎關(guān)注我的微信訂閱號:



歡迎關(guān)注我的開發(fā)者頭條獨(dú)家號搜索:269166

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市铭若,隨后出現(xiàn)的幾起案子洪碳,更是在濱河造成了極大的恐慌,老刑警劉巖叼屠,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞳腌,死亡現(xiàn)場離奇詭異,居然都是意外死亡镜雨,警方通過查閱死者的電腦和手機(jī)嫂侍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人挑宠,你說我怎么就攤上這事菲盾。” “怎么了各淀?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵懒鉴,是天一觀的道長。 經(jīng)常有香客問我碎浇,道長临谱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任南捂,我火速辦了婚禮吴裤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘溺健。我一直安慰自己麦牺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布鞭缭。 她就那樣靜靜地躺著剖膳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪岭辣。 梳的紋絲不亂的頭發(fā)上吱晒,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機(jī)與錄音沦童,去河邊找鬼仑濒。 笑死,一個胖子當(dāng)著我的面吹牛偷遗,可吹牛的內(nèi)容都是我干的墩瞳。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼氏豌,長吁一口氣:“原來是場噩夢啊……” “哼喉酌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起泵喘,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤泪电,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后纪铺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體相速,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年霹陡,在試婚紗的時候發(fā)現(xiàn)自己被綠了和蚪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片止状。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡烹棉,死狀恐怖攒霹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情浆洗,我是刑警寧澤催束,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站伏社,受9級特大地震影響抠刺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜摘昌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一速妖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧聪黎,春花似錦罕容、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至喉镰,卻和暖如春旅择,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背侣姆。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工生真, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人捺宗。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓柱蟀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親偿凭。 傳聞我的和親對象是個殘疾皇子产弹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容