Elasticsearch 是一個(gè)基于 Apache Lucene 的開源搜索和分析引擎,允許用戶近實(shí)時(shí)地存儲(chǔ)、搜索和分析數(shù)據(jù)。Pronto 是 eBay 托管 Elasticsearch 集群的平臺(tái),使 eBay 內(nèi)部客戶易于部署、運(yùn)維和擴(kuò)展 Elasticsearch 以進(jìn)行全文搜索、實(shí)時(shí)分析和日志事件監(jiān)控。今天 Pronto 管理著 60 多個(gè) Elasticsearch 集群,達(dá) 2000 多個(gè)節(jié)點(diǎn)。日采集數(shù)據(jù)量達(dá)到 180 億個(gè)文檔,日均查詢量達(dá)到 35 億。該平臺(tái)提供了創(chuàng)建、修復(fù)、安全、監(jiān)控、告警和診斷的一整套功能。
雖然 Elasticsearch 專為快速查詢而設(shè)計(jì),但其性能在很大程度上取決于應(yīng)用程序的場(chǎng)景、索引的數(shù)據(jù)量以及應(yīng)用程序和用戶查詢數(shù)據(jù)的速度。本文總結(jié)了 Pronto 團(tuán)隊(duì)面臨的挑戰(zhàn)以及應(yīng)對(duì)挑戰(zhàn)所構(gòu)建的流程和工具,還給出了對(duì)幾種配置進(jìn)行基準(zhǔn)測(cè)試的一些結(jié)果。
迄今遇到的 Pronto/Elasticsearch 使用場(chǎng)景所面臨的挑戰(zhàn)包括:
高吞吐量:一些集群每天采集的數(shù)據(jù)高達(dá) 5TB,一些集群每天的搜索請(qǐng)求超過 4 億。如果 Elasticsearch 無法及時(shí)處理這些請(qǐng)求,上游的請(qǐng)求將發(fā)生積壓。
低搜索延遲:對(duì)于性能比較關(guān)鍵的集群,尤其是面向線上的系統(tǒng),低搜索延遲是必需的,否則用戶體驗(yàn)將受到影響。
由于數(shù)據(jù)或查詢是可變的,所以最佳設(shè)置也是變化的。不存在所有情況都是最佳的設(shè)置。例如,將索引拆分成更多的分片對(duì)于費(fèi)時(shí)的查詢是有好處的,但是這可能會(huì)影響其他查詢性能。
為了幫助我們的客戶應(yīng)對(duì)這些挑戰(zhàn),Pronto 團(tuán)隊(duì)為用戶案例上線和整個(gè)集群生命周期,針對(duì)性能測(cè)試、調(diào)優(yōu)和監(jiān)控構(gòu)建了一套策略方法。
預(yù)估集群大?。涸谛碌挠脩舭咐暇€之前,收集客戶提供的信息,如吞吐量、文檔大小、文檔數(shù)量和搜索類型,以估計(jì) Elasticsearch 集群的初始大小。
優(yōu)化索引設(shè)計(jì):與客戶一起評(píng)估索引設(shè)計(jì)。
索引性能調(diào)優(yōu):根據(jù)用戶場(chǎng)景進(jìn)行索引性能和搜索性能調(diào)優(yōu)。
搜索性能調(diào)優(yōu):使用用戶真實(shí)數(shù)據(jù) / 查詢運(yùn)行性能測(cè)試,比較和分析不同 Elasticsearch 配置參數(shù)的測(cè)試結(jié)果。
運(yùn)行性能測(cè)試:在用戶案例上線后,集群將受到監(jiān)控,并且每當(dāng)數(shù)據(jù)發(fā)生變化,查詢更改或流量增加時(shí),用戶都可以自由地重新運(yùn)行性能測(cè)試。
Pronto 團(tuán)隊(duì)為每種類型的機(jī)器和每個(gè)支持的 Elasticsearch 版本運(yùn)行基準(zhǔn)測(cè)試,收集性能數(shù)據(jù),然后結(jié)合客戶提供的信息,估算群集初始大小,包括:
索引吞吐量
文檔大小
搜索吞吐量
查詢類型
熱點(diǎn)索引文檔數(shù)量
保留策略
響應(yīng)時(shí)間需求
SLA 級(jí)別
在開始索引數(shù)據(jù)和運(yùn)行查詢之前,我們先考慮一下。索引到底表示什么?Elastic 的官方答案是“具有某種相似特征的文檔集合”。因此,下一個(gè)問題是“應(yīng)該使用哪些特征來對(duì)數(shù)據(jù)進(jìn)行分組?應(yīng)該把所有文檔放入一個(gè)索引還是多個(gè)索引?”,答案是,這取決于使用的查詢。以下是關(guān)于如何根據(jù)最常用的查詢組織索引的一些建議。
如果查詢有一個(gè)過濾字段并且它的值是可枚舉的,那么把數(shù)據(jù)分成多個(gè)索引。例如,你有大量的全球產(chǎn)品信息被采集到 Elasticsearch 中,大多數(shù)查詢都有一個(gè)過濾條件“地區(qū)”,并且很少有機(jī)會(huì)運(yùn)行跨地區(qū)查詢。如下查詢體可以被優(yōu)化:
{ 'query': { 'bool': { 'must': { 'match': { 'title': '${title}' } }, 'filter': { 'term': { 'region': 'US' } } } }}
在這種情況下,如果索引按照美國(guó)、歐盟等地區(qū)分成幾個(gè)較小的索引,可以從查詢中刪除過濾子句,查詢性能會(huì)更好。如果我們需要運(yùn)行一個(gè)跨地區(qū)查詢,我們可以將多個(gè)索引或通配符傳遞給 Elasticsearch。
如果查詢有一個(gè)過濾字段并且它的值是不可枚舉的,建議使用路由。通過使用過濾字段值作為路由鍵,我們可以將具有相同過濾字段值的文檔索引至同一個(gè)分片,并移除過濾子句。
例如,Elasticsearch 集群中存有數(shù)以百萬記的訂單數(shù)據(jù),大多數(shù)查詢都包含有買方 ID 作為限定從句。為每個(gè)買家創(chuàng)建索引是不可能的,所以我們不能通過買方 ID 將數(shù)據(jù)拆分成多個(gè)索引。一個(gè)合適的解決方案是,使用路由將具有相同買方 ID 的所有訂單放入相同分片中。然后幾乎所有的查詢都可以在匹配路由鍵的分片內(nèi)完成。
如果查詢具有日期范圍過濾子句,則按日期建立數(shù)據(jù)。這適用于大多數(shù)日志記錄和監(jiān)控場(chǎng)景。我們可以按天、周或月組織索引,然后可以獲得指定的日期范圍內(nèi)的索引列表,這樣,Elasticsearch 只需要查詢一個(gè)較小的數(shù)據(jù)集而不是整個(gè)數(shù)據(jù)集。另外,當(dāng)數(shù)據(jù)過期時(shí),刪除舊的索引也很容易。
明確設(shè)置映射。雖然 Elasticsearch 可以動(dòng)態(tài)創(chuàng)建映射,但創(chuàng)建的映射可能并不適用于所有場(chǎng)景。例如,Elasticsearch 5.x 中的默認(rèn)字符串字段映射是“keyword”和“text”類型。這在很多情況下是沒有必要的。
如果文檔使用用戶定義的 ID 或路由進(jìn)行索引,要避免造成分片不平衡。 Elasticsearch 使用隨機(jī) ID 生成器和散列算法來確保文檔均勻地分配給分片。當(dāng)使用用戶定義的 ID 或路由時(shí),ID 或路由鍵可能不夠隨機(jī),造成一些分片明顯比其他分片大。在這種情況下,這個(gè)分片上的讀 / 寫操作會(huì)比其他的慢得多。我們可以優(yōu)化 ID/ 路由鍵或使用 index.routing_partition_size(5.3 和更高版本中可用)。
確保分片均勻分布在節(jié)點(diǎn)上。一個(gè)節(jié)點(diǎn)如果比其他節(jié)點(diǎn)的分片多,則會(huì)比其他節(jié)點(diǎn)承擔(dān)更多的負(fù)載,成為整個(gè)系統(tǒng)的瓶頸。
對(duì)于日志記錄和監(jiān)控等重度索引場(chǎng)景,索引性能是關(guān)鍵指標(biāo)。這里有一些建議:

性能和刷新時(shí)間間隔之間的關(guān)系
從上圖可以看出,隨著刷新時(shí)間間隔的增加,吞吐量增加,響應(yīng)時(shí)間減少。我們可以使用下面的請(qǐng)求來檢查我們有多少段以及刷新和合并花了多少時(shí)間。
Index/_stats?filter_path= indices..refresh,indices..segments,indices.**.merges
減少副本數(shù)量。對(duì)于每個(gè)索引請(qǐng)求,Elasticsearch 需要將文檔寫入主分片和所有副本分片。顯然,副本過多會(huì)減慢索引速度,但另一方面,這將提高搜索性能。我們將在本文后面討論這個(gè)問題。

性能和副本數(shù)之間的關(guān)系
從上圖可以看出,隨著副本數(shù)增加,吞吐量下降,響應(yīng)時(shí)間增加。
盡可能使用自動(dòng)生成的 ID。 Elasticsearch 自動(dòng)生成的 ID 保證是唯一的,能避免版本查找。如果客戶真的需要使用自定義的 ID,我們建議選擇一個(gè)對(duì) Lucene 友好的 ID,比如零填充順序 ID、UUID-1 或者納秒級(jí)時(shí)間。這些 ID 具有一致的順序模式,能良好壓縮。相比之下,像 UUID-4 這樣的 ID 本質(zhì)上是隨機(jī)的,壓縮率低,會(huì)降低 Lucene 的速度。
使用 Elasticsearch 的主要原因是支持搜索數(shù)據(jù)。用戶應(yīng)該能夠快速找到他們正在尋找的信息。搜索性能取決于很多因素。
盡可能使用過濾器上下文(Filter)替代查詢上下文(Query)。 查詢子句用于回答“這個(gè)文檔與此子句相匹配的程度”,而過濾器子句用于回答“這個(gè)文檔是否匹配這個(gè)子句”,Elasticsearch 只需要回答“是”或“否”,不需要為過濾器子句計(jì)算相關(guān)性分?jǐn)?shù),而且過濾器結(jié)果可以緩存。有關(guān)詳細(xì)信息,請(qǐng)參閱查詢和過濾上下文。

查詢和過濾器性能比較
增加刷新時(shí)間間隔。正如我們?cè)谒饕阅苷{(diào)優(yōu)中所提到的,Elasticsearch 每次刷新時(shí)都會(huì)創(chuàng)建一個(gè)新的段。增加刷新時(shí)間間隔將有助于減少段數(shù)并降低搜索的 IO 成本。并且,一旦發(fā)生刷新并且數(shù)據(jù)改變,緩存將會(huì)失效。增加刷新時(shí)間間隔可以使 Elasticsearch 更高效地利用緩存。
增加副本數(shù)。 Elasticsearch 可以在主分片或副本分片上執(zhí)行搜索。副本越多,搜索可用的節(jié)點(diǎn)就越多。

搜索性能和副本數(shù)之間的關(guān)系
從上圖可以看出,搜索吞吐量幾乎與副本數(shù)量成線性關(guān)系。注意在這個(gè)測(cè)試中,測(cè)試集群有足夠的數(shù)據(jù)節(jié)點(diǎn)來確保每個(gè)分片都有一個(gè)專有節(jié)點(diǎn)。如果這個(gè)條件不能滿足,搜索吞吐量就不會(huì)這么好。
嘗試不同的分片數(shù)?!皯?yīng)該為索引設(shè)置多少分片呢?”這可能是最常見的問題。遺憾的是,沒有適合所有應(yīng)用場(chǎng)景的分片數(shù)。這完全取決于你的情況。
分片太少會(huì)使搜索無法擴(kuò)展。例如,如果分片數(shù)設(shè)置為 1,則索引中的所有文檔都將存儲(chǔ)在一個(gè)分片中。對(duì)于每個(gè)搜索,只有一個(gè)節(jié)點(diǎn)能夠參與計(jì)算。如果索引中的文件數(shù)量很多,查詢會(huì)很耗時(shí)。從另一方面來說,創(chuàng)建的索引分片太多也會(huì)對(duì)性能造成不利影響,因?yàn)?Elasticsearch 需要在所有分片上運(yùn)行查詢(除非在請(qǐng)求中指定了路由鍵),然后提取并合并所有返回的結(jié)果。
根據(jù)我們的經(jīng)驗(yàn),如果索引小于 1G,可以將分片數(shù)設(shè)置為 1。對(duì)于大多數(shù)場(chǎng)景,我們可以將分片數(shù)保留為默認(rèn)值 5,但是如果分片大小超過 30GB,我們應(yīng)該增加分片 ,將索引分成更多的分片。創(chuàng)建索引后,分片數(shù)不能更改,但是我們可以創(chuàng)建新的索引并使用 reindex API 遷移數(shù)據(jù)。
我們測(cè)試了一個(gè)擁有 1 億個(gè)文檔,大約 150GB 的索引。我們使用了 100 個(gè)線程發(fā)送搜索請(qǐng)求。

搜索性能和分片數(shù)量之間的關(guān)系
從上圖可以看出,最優(yōu)的分片數(shù)量為 11 個(gè)。開始時(shí)搜索吞吐量增大(響應(yīng)時(shí)間減少),但隨著分片數(shù)量的增加,搜索吞吐量減小(響應(yīng)時(shí)間增加)。
請(qǐng)注意,在這個(gè)測(cè)試中,就像在副本數(shù)測(cè)試中一樣,每個(gè)分片都有一個(gè)獨(dú)占節(jié)點(diǎn)。如果這個(gè)條件不能滿足,搜索吞吐量將不會(huì)像這個(gè)圖那樣好。
在這種情況下,我們建議你嘗試一個(gè)小于最優(yōu)值的分片數(shù),因?yàn)槿绻制?,并且使每個(gè)分片都有一個(gè)獨(dú)占數(shù)據(jù)節(jié)點(diǎn),那么就需要很多節(jié)點(diǎn)。
節(jié)點(diǎn)查詢緩存。節(jié)點(diǎn)查詢緩存只緩存過濾器上下文中使用的查詢。與查詢子句不同,過濾子句是“是”或“否”的問題。Elasticsearch 使用位集(bit set)機(jī)制來緩存過濾結(jié)果,以便后面使用相同的過濾器的查詢進(jìn)行加速。請(qǐng)注意,Elasticsearch 只對(duì)保存超過 10,000(或文檔總數(shù)的 3%,以較大者為準(zhǔn))個(gè)文檔的段啟用查詢緩存。有關(guān)更多詳細(xì)信息,請(qǐng)參閱緩存(文末附鏈接)。
我們可以使用下面的請(qǐng)求來檢查一個(gè)節(jié)點(diǎn)查詢緩存是否生效。
GET index_name/_stats?filter_path=indices.**.query_cache{ 'indices': { 'index_name': { 'primaries': { 'query_cache': { 'memory_size_in_bytes': 46004616, 'total_count': 1588886, 'hit_count': 515001, 'miss_count': 1073885, 'cache_size': 630, 'cache_count': 630, 'evictions': 0 } }, 'total': { 'query_cache': { 'memory_size_in_bytes': 46004616, 'total_count': 1588886, 'hit_count': 515001, 'miss_count': 1073885, 'cache_size': 630, 'cache_count': 630, 'evictions': 0 } } } }}
分片查詢緩存。如果大多數(shù)查詢是聚合查詢,我們應(yīng)該考慮分片查詢緩存。分片查詢緩存可以緩存聚合結(jié)果,以便 Elasticsearch 以低開銷直接處理請(qǐng)求。有幾件事情需要注意:
設(shè)置“size”為 0。分片查詢緩存只緩存聚合結(jié)果和建議。它不會(huì)緩存命中,因此如果將 size 設(shè)置為非零,則無法從緩存中獲益。
查詢請(qǐng)求的負(fù)載(Payload)必須完全相同。分片查詢緩存使用請(qǐng)求負(fù)載作為緩存鍵,因此需要確保后續(xù)查詢請(qǐng)求的負(fù)載必須和之前的完全一致。由于負(fù)載中 JSON 鍵的順序變化會(huì)導(dǎo)致負(fù)載變化,故建議對(duì)負(fù)載的鍵進(jìn)行排序來確保順序一致。
處理好日期時(shí)間。不要直接在查詢中使用像 Date.now 這樣的變量。否則,每個(gè)請(qǐng)求的請(qǐng)求體都不同,從而導(dǎo)致緩存始終無效。我們建議將日期時(shí)間整理為小時(shí)或天,以便更有效地利用緩存。
我們可以使用下面的請(qǐng)求來檢查分片查詢緩存是否有效。
GET index_name/_stats?filter_path=indices.**.request_cache{ 'indices': { 'index_name': { 'primaries': { 'request_cache': { 'memory_size_in_bytes': 0, 'evictions': 0, 'hit_count': 541, 'miss_count': 514098 } }, 'total': { 'request_cache': { 'memory_size_in_bytes': 0, 'evictions': 0, 'hit_count': 982, 'miss_count': 947321 } } } }}
僅檢索必要的字段。如果文檔很大,而你只需要幾個(gè)字段,請(qǐng)使用 stored_fields 檢索需要的字段而不是所有字段。
避免搜索停用詞。諸如“a”和“the”等停用詞可能導(dǎo)致查詢命中結(jié)果數(shù)暴增。假設(shè)你有一百萬個(gè)文檔。搜索“fox”可能會(huì)返回幾十個(gè)命中文檔,但搜索“the fox”可能會(huì)返回索引中的所有文檔,因?yàn)椤皌he”幾乎出現(xiàn)在所有文檔中。Elasticsearch 需要對(duì)所有命中的結(jié)果進(jìn)行評(píng)分和排序,以致像“the fox”這樣的查詢會(huì)減慢整個(gè)系統(tǒng)。你可以使用停用詞過濾器來刪除停用詞,或使用“and”運(yùn)算符將查詢更改為“the AND fox”,獲得更精確的結(jié)果。
如果某些單詞在索引中經(jīng)常使用,但不在默認(rèn)停用詞列表中,則可以使用截止頻率來動(dòng)態(tài)處理它們。
如果不關(guān)心文檔返回的順序,則按 _doc 排序。 Elasticsearch 默認(rèn)使用“_score”字段按評(píng)分排序。如果不關(guān)心順序,可以使用'sort':'_doc'讓 Elasticsearch 按索引順序返回命中文檔,可以節(jié)省排序開銷。
避免使用腳本查詢(script query)計(jì)算動(dòng)態(tài)字段,建議在索引時(shí)計(jì)算并在文檔中添加該字段。例如,我們有一個(gè)包含大量用戶信息的索引,我們需要查詢以'1234'開頭的所有用戶。你可能運(yùn)行一個(gè)腳本查詢,如'source':'doc['num'].value.startsWith('1234')'。這個(gè)查詢非常耗費(fèi)資源,并且減慢整個(gè)系統(tǒng)。索引時(shí)考慮添加一個(gè)名為“num_prefix”的字段。然后我們可以查詢'name_prefix':'1234'。
避免使用通配符查詢。
對(duì)于每一次變更,都需要運(yùn)行性能測(cè)試來驗(yàn)證變更是否適用。因?yàn)?Elasticsearch 是一個(gè) RESTful 服務(wù),所以可以使用 Rally、Apache Jmeter 和 Gatling 等工具來運(yùn)行性能測(cè)試。因?yàn)?Pronto 團(tuán)隊(duì)需要在每種類型的機(jī)器和 Elasticsearch 版本上運(yùn)行大量的基準(zhǔn)測(cè)試,而且需要在許多 Elasticsearch 集群上針對(duì)不同 Elasticsearch 配置參數(shù)運(yùn)行性能測(cè)試,所以這些工具不能滿足我們的要求。
Pronto 團(tuán)隊(duì)建立了基于 Gatling 的在線性能分析服務(wù),幫助客戶和我們運(yùn)行性能測(cè)試和回歸測(cè)試。該服務(wù)提供的功能有:
輕松添加和編輯測(cè)試。用戶無需 Gatling 或 Scala 知識(shí)即可根據(jù)輸入的查詢或文檔結(jié)構(gòu)生成測(cè)試。
順序運(yùn)行多個(gè)測(cè)試,無需人工干預(yù)。該服務(wù)可以檢查狀態(tài)并在每次測(cè)試之前 / 之后更改 Elasticsearch 設(shè)置。
幫助用戶比較和分析測(cè)試結(jié)果。測(cè)試期間的測(cè)試結(jié)果和集群統(tǒng)計(jì)信息將保留下來,并可以通過預(yù)定義的 Kibana 可視化進(jìn)行分析。
從命令行或 Web UI 運(yùn)行測(cè)試。該服務(wù)提供了與其他系統(tǒng)集成的 Rest API。
架構(gòu)如下:

性能測(cè)試服務(wù)架構(gòu)
用戶可以查看每個(gè)測(cè)試的 Gatling 報(bào)告,并查看 Kibana 預(yù)定義的可視化圖像,以便進(jìn)一步分析和比較,如下所示。


本文總結(jié)了在設(shè)計(jì)滿足高期望的采集和搜索性能的 Elasticsearch 集群時(shí)應(yīng)該考慮的索引 / 分片 / 副本設(shè)計(jì)以及一些其他配置,還說明了 Pronto 如何在策略上幫助客戶進(jìn)行初始規(guī)模調(diào)整、索引設(shè)計(jì)和調(diào)優(yōu)以及性能測(cè)試。截至今天,Pronto 團(tuán)隊(duì)已經(jīng)幫助包括訂單管理系統(tǒng)(OMS)和搜索引擎優(yōu)化(SEO)在內(nèi)的眾多客戶實(shí)現(xiàn)了苛刻的性能目標(biāo),從而為 eBay 的關(guān)鍵業(yè)務(wù)作出了貢獻(xiàn)。
Elasticsearch 的性能取決于很多因素,包括文檔結(jié)構(gòu)、文檔大小、索引設(shè)置 / 映射、請(qǐng)求率、數(shù)據(jù)集大小和查詢命中次數(shù)等等。針對(duì)一種情況的建議不一定適用于另一種情況,因此,徹底進(jìn)行性能測(cè)試、收集數(shù)據(jù)、根據(jù)負(fù)載調(diào)整配置以及優(yōu)化集群以滿足性能要求非常重要。
查看英文原文:https://www./stories/blogs/tech/elasticsearch-performance-tuning-practice-at-ebay/
今日薦文
點(diǎn)擊下方圖片即可閱讀
雖然這兩天比特幣暴跌,但我想從歷史角度冷靜聊聊區(qū)塊鏈
隨著互聯(lián)網(wǎng)業(yè)務(wù)的飛速發(fā)展,系統(tǒng)動(dòng)輒要支持億級(jí)流量壓力,架構(gòu)設(shè)計(jì)不斷面臨新的挑戰(zhàn)。海量系統(tǒng)設(shè)計(jì)、容災(zāi)、健壯性,架構(gòu)師要考慮多方面的需求做出權(quán)衡。不如來聽聽國(guó)內(nèi)外知名互聯(lián)網(wǎng)公司的架構(gòu)師分享架構(gòu)設(shè)計(jì)背后的挑戰(zhàn)與問題解決之道。QCon 北京 2018 目前 8 折報(bào)名中,立減 1360 元,有任何問題歡迎咨詢購(gòu)票經(jīng)理 Hanna,電話:15110019061,微信:qcon-0410。