您好,登錄后才能下訂單哦!
英文原文:https://qbox.io/blog/elasticsearch-search-tuning-5-0-ultimate-guide
作者:Adam Vanderbush
譯者:楊振濤
目錄
Elasticsearch搜索調(diào)優(yōu)權(quán)威指南,是QBOX在其博客上發(fā)布的系列文章之一,本文是該系列的第一篇,主要從文檔建模、內(nèi)存分配、文件系統(tǒng)緩存、GC和硬件等方面介紹了優(yōu)化查詢性能的一些經(jīng)驗。
Elasticsearch 5.0.0確實是在2.x之后的一個大版本,為大家?guī)砹嗽S多新東西。Elasticsearch現(xiàn)在作為Elastic Stack中的一員,與整個技術(shù)棧的其他產(chǎn)品的版本號已經(jīng)對齊,現(xiàn)在Kibana、Logstash、Beats和Elasticsearch全都是5.0版本了。
這個版本的Elasticsearch是目前為止最快、最安全、最彈性,也是最易用的,而且還帶來了很多的改進和新特性。
我們已經(jīng)通過“Elasticsearch性能調(diào)優(yōu)權(quán)威指南”系列,介紹了一些性能調(diào)優(yōu)的基本經(jīng)驗和方法,解釋了每一步最關(guān)鍵的系統(tǒng)設置和衡量指標。該系列共分下列3個部分:
索引決策也很重要,它對如何搜索數(shù)據(jù)有很大的影響。如果是一個字符串字段,是否需要分詞或歸一化?如果是,怎么做?如果是一個數(shù)值型屬性,需要哪種精度?還有很多其他類型,比如date-time、geospatial shape以及父子關(guān)系等,需要更多特別的考慮。
我們也通過一個系列教程討論了“Elasticsearch索引性能優(yōu)化”,介紹了一些通用的技巧和方法,來最大化索引的吞吐量并降低監(jiān)控和管理的負載。該教程分如下3個部分:
本文旨在推薦一些搜索調(diào)優(yōu)技術(shù)、策略以及Elasticsearch 5.0及以上的推薦特性。
內(nèi)部對象屬性數(shù)組并不像期望的那樣工作。Lucene?中沒有內(nèi)部對象的概念,所以Elasticsearch把對象層次展開到一個由屬性名稱和屬性值組成的簡單列表中。以下列文檔為例:
curl -XPUT 'localhost:9200/my_index/my_type/1?pretty' -H 'Content-Type: application/json' -d '{
"group" : "fans",
"user" : [
{
"first" : "John",
"last" : "Smith"
},
{
"first" : "Alice",
"last" : "White"
}
]
}'
該請求會在內(nèi)部轉(zhuǎn)換為如下的文檔形式:
{
"group" : "fans",
"user.first" : [ "alice", "john" ],
"user.last" : [ "smith", "white" ]
}
如果需要索引對象數(shù)組,并維護數(shù)組中每個對象的依賴關(guān)系,應當使用內(nèi)嵌數(shù)據(jù)類型而不是對象數(shù)據(jù)類型。內(nèi)嵌對象在內(nèi)部會把數(shù)組中的每個對象當作單獨的隱藏文檔來索引,即使用下述內(nèi)嵌查詢,可以單獨查詢每個內(nèi)嵌對象:
curl -XPUT 'ES_HOST:ES_PORT/my_index?pretty' -H 'Content-Type: application/json' -d '{
"mappings": {
"my_type": {
"properties": {
"user": {
"type": "nested"
}
}
}
}
}'
curl -XPUT 'ES_HOST:ES_PORT/my_index/my_type/1?pretty' -H 'Content-Type: application/json' -d '{
"group" : "fans",
"user" : [
{
"first" : "John",
"last" : "Smith"
},
{
"first" : "Alice",
"last" : "White"
}
]
}'
curl -XGET 'ES_HOST:ES_PORT/my_index/_search?pretty' -H 'Content-Type: application/json' -d '{
"query": {
"nested": {
"path": "user",
"query": {
"bool": {
"must": [
{ "match": { "user.first": "Alice" }},
{ "match": { "user.last": "Smith" }}
]
}
}
}
}
}'
curl -XGET 'ES_HOST:ES_PORT/my_index/_search?pretty' -H 'Content-Type: application/json' -d '{
"query": {
"nested": {
"path": "user",
"query": {
"bool": {
"must": [
{ "match": { "user.first": "Alice" }},
{ "match": { "user.last": "White" }}
]
}
},
"inner_hits": {
"highlight": {
"fields": {
"user.first": {}
}
}
}
}
}
}'
當有一個主實體比如一篇博客文章,帶有一些有一定關(guān)系但又不是非常重要的其他實體比如評論時,內(nèi)嵌對象會非常有用。如果能根據(jù)評論內(nèi)容來查詢到博客文章,那就很不錯,而且內(nèi)嵌查詢和過濾器一起提供了更快的join查詢能力。
內(nèi)嵌對象模型的缺點如下:
為了 增加 、修改 或 刪除 一個內(nèi)嵌對象文檔,整個文檔必須重建索引;這就導致內(nèi)嵌文檔越多開銷就越大。
搜索請求返回整個文檔,而不是只返回匹配的內(nèi)嵌文檔。雖然已經(jīng)以后計劃支持返回根文檔的部分最配內(nèi)嵌文檔,但目前仍然不支持。
有時候可能需要把主文檔和其關(guān)聯(lián)實體分離,這種分離由父子關(guān)系來提供。
通過建立另一個文檔的父類型mapping,可以在相同索引的文檔之間建立父子關(guān)系:
curl -XPUT 'ES_HOST:ES_PORT/my_index?pretty' -H 'Content-Type: application/json' -d '{
"mappings": {
"my_parent": {},
"my_child": {
"_parent": {
"type": "my_parent"
}
}
}
}'
curl -XPUT 'ES_HOST:ES_PORT/my_index/my_parent/1?pretty' -H 'Content-Type: application/json' -d '{
"text": "This is a parent document"
}'
curl -XPUT 'ES_HOST:ES_PORT/my_index/my_child/2?parent=1&pretty' -H 'Content-Type: application/json' -d '{
"text": "This is a child document"
}'
curl -XPUT 'ES_HOST:ES_PORT/my_index/my_child/3?parent=1&refresh=true&pretty' -H 'Content-Type: application/json' -d '{
"text": "This is another child document"
}'
curl -XGET 'ES_HOST:ES_PORT/my_index/my_parent/_search?pretty' -H 'Content-Type: application/json' -d '{
"query": {
"has_child": {
"type": "my_child",
"query": {
"match": {
"text": "child document"
}
}
}
}
}'
父子join對管理實體關(guān)系非常有用,尤其是在索引時間比檢索時間很重要的情形下,但是它會帶來較大的開銷;父子查詢比同等的內(nèi)嵌查詢要慢5到10倍。
父子關(guān)系使用了全局序列號來加速join操作。無論父子map是否使用了內(nèi)存緩存或磁盤上的doc value,全局序列號仍然需要在索引發(fā)生任何改變時進行重建。
分片中的父代越多,全局序列號構(gòu)建就越耗時。相對于需要父代和較少的子代, 父子關(guān)系最適合每個父代有很多子代的情形。
全局序列號默認是 延遲 構(gòu)建:refresh后的第一個父子查詢或聚合請求將會觸發(fā)構(gòu)建全局序列號。這會讓用戶感知到一個明顯的潛在峰值??梢允褂胑ager_global_ordinals 來把查詢期構(gòu)建全局序列號的成本轉(zhuǎn)移到refresh期,通過如下方式mapping _parent屬性:
curl -XPUT 'ES_HOST:ES_PORT/company -d ‘{
"mappings": {
"branch": {},
"employee": {
"_parent": {
"type": "branch",
"fielddata": {
"loading": "eager_global_ordinals"
}
}
}
}
}’
這里,_parent屬性的全局序列號將會在一個新的段搜索可見時被構(gòu)建。
對于很多的父代,全局序列號要花費數(shù)秒鐘來構(gòu)建。此時,需要增加refresh_interval,以便refresh的頻率更低,而全局序列號保持可用的時間更長。這將大幅減少每秒鐘重建全局序列號的CPU消耗。
對多代數(shù)據(jù)的Join(參考Grandparents and Grandchildren)能力聽起來很吸引人,但需要思考其代價:
對于運行中Elasticsearch,內(nèi)存是需要密切監(jiān)控的重要資源之一。Elasticsearch和Lucene通過JVM堆內(nèi)存和文件系統(tǒng)緩存兩種方式來消耗內(nèi)存。由于Elasticsearch運行在Java虛擬機(JVM)中,所以JVM的GC周期和頻率也需要重點監(jiān)控。
JVM堆內(nèi)存
對于Elasticsearch一個“剛好合適”的JVM堆大小是非常重要的——不能設置過大或過小,原因見后文。一般來說Elasticsearch的經(jīng)驗值是分配少于50%的可用RAM給JVM堆,且不要超過32GB。
為Elasticsearch分配過少的堆內(nèi)存,那么就會留給Lucene更多內(nèi)存,而Lucene重度依賴于文件系統(tǒng)緩存來快速處理請求。不管怎樣也不能設置過小的堆內(nèi)存,因為當應用由于頻繁GC而面臨短時中斷時,可能會遭遇內(nèi)存溢出錯誤或吞吐量下降。
Elasticsearch默認安裝時設置的JVM堆大小為1GB,這在大多數(shù)情況下都偏小。可以通過環(huán)境變量來設置期望的對大小并重啟Elasticsearch:
export ES_HEAP_SIZE=10g
設置JVM堆大小的另一種方式(相當于設置一樣的最小值和最大值,以防止重新調(diào)整堆大?。窃诿看螁覧lasticsearch時通過命令行參數(shù)指定:
ES_HEAP_SIZE="10g" ./bin/elasticsearch
這兩種示例方式都是設置了10GB的堆大小,為了驗證是否設置成功,執(zhí)行:
curl -XGET http://ES_HOST:9200/_cat/nodes?h=heap.max
返回的輸出會顯示已正確地更新了最大堆內(nèi)存。
垃圾回收
Elasticsearch依靠GC過程來釋放堆內(nèi)存。由于GC本身也要消耗資源(為了釋放資源?。?,所以應當留意GC頻率和持續(xù)時間,以確認是否需要調(diào)整堆內(nèi)存大小。設置過大的堆內(nèi)存,換來的是更長的GC時間;這種過多的停頓非常危險,因為可能導致集群誤認為該節(jié)點網(wǎng)絡異常而失聯(lián)。
因此,Elasticsearch重度依賴文件系統(tǒng)緩存來加速搜索。一般需要保證至少有一半的可用內(nèi)存用于文件系統(tǒng)緩存,這樣Elasticsearch才能保持索引數(shù)據(jù)的熱點區(qū)域都在物理內(nèi)存中。
使用更快的硬件
如果搜索受限于I/O,應當考慮為文件系統(tǒng)緩存分片更多內(nèi)存(參考前文),或者購買更快的驅(qū)動。特別地,SSD公認地比機械磁盤性能好很多。盡可能使用本地存儲,避免使用像 NFS 或 SMB 之類的遠程或網(wǎng)絡文件系統(tǒng),也要注意像Amazon EBS這樣的虛擬化存儲。
Elasticsearch使用虛擬化存儲工作是沒有問題的,它因為快速和安裝簡單而受歡迎,但同樣不幸的是,在基礎上與專用的本地存儲相比它天生就比較慢。如果在EBS上創(chuàng)建了一個索引庫,請確認使用預分配的IOPS,否則很快就會被限流。
如果搜索受限于CPU,那么應當考慮購買更快的CPU。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。