Elasticsearch是一款流行的分佈式開源搜索和數據分析引擎,具備高性能、易擴展、容錯性強等特點。它強化了Apache Lucene的搜索能力,把掌控海量數據索引和查詢的方式提升到一個新的層次。
本文結合開源社區和阿里雲平臺的實踐經驗,探討如何調優Elasticsearch的性能,提高索引和查詢吞吐量。
/ 前文回顧 /
Elasticsearch索引和查詢性能調優的21條建議【上】
點擊查看大圖
查詢性能調優建議
01
使用過濾器緩存和分片查詢緩存
默認情況下,Elasticsearch的查詢會計算返回的每條數據與查詢語句的相關度,但對於非全文索引的使用場景,用戶並不關心查詢結果與查詢條件的相關度,只是想精確地查找目標數據。此時,可以通過filter來讓Elasticsearch不計算評分,並且儘可能地緩存filter的結果集,供後續包含相同filter的查詢使用,提高查詢效率。
普通查詢
curl -XGET "http://localhost:9200/twitter/_search" -H 'Content-Type: application/json' -d'
{
"query": {
"match": {
"user": "kimchy"
}
}
}'
過濾器(filter)查詢
curl -XGET "http://localhost:9200/twitter/_search" -H 'Content-Type: application/json' -d'
{
"query": {
"bool": {
"filter": {
"match": {
"user": "kimchy"
}
}
}
}
}'
分片查詢緩存的目的是緩存聚合、提示詞結果和命中數(它不會緩存返回的文檔,因此,它只在search_type=count時起作用)。
通過下面的參數我們可以設置分片緩存的大小,默認情況下是JVM堆的1%大小,當然我們也可以手動設置在config/elasticsearch.yml文件裏:
indices.requests.cache.size: 1%
查看緩存佔用內存情況
(name表示節點名, query_cache表示過濾器緩存,request_cache表示分片緩存,fielddata表示字段數據緩存,segments表示索引段)
curl -XGET "http://localhost:9200/_cat/nodes?h=name,query_cache.memory_size,request_cache.memory_size,fielddata.memory_size,segments.memory&v"
02
使用路由routing
Elasticsearch寫入文檔時,文檔會通過一個公式路由到一個索引中的一個分片上。默認的公式如下:
shard_num = hash(_routing) % num_primary_shards
_routing字段的取值,默認是_id字段,可以根據業務場景設置經常查詢的字段作爲路由字段。例如可以考慮將用戶id、地區作爲路由字段,查詢時可以過濾不必要的分片,加快查詢速度。
寫入時指定路由
curl -XPUT "http://localhost:9200/my_index/my_type/1?routing=user1" -H 'Content-Type: application/json' -d'
{
"title": "This is a document",
"author": "user1"
}'
查詢時不指定路由,需要查詢所有分片
curl -XGET "http://localhost:9200/my_index/_search" -H 'Content-Type: application/json' -d'
{
"query": {
"match": {
"title": "document"
}
}
}'
返回結果
{
"took": 2,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
}
......
}
查詢時指定路由,只需要查詢1個分片
curl -XGET "http://localhost:9200/my_index/_search?routing=user1" -H 'Content-Type: application/json' -d'
{
"query": {
"match": {
"title": "document"
}
}
}'
返回結果
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
}
......
}
03
強制合併只讀索引
關閉歷史數據索引
只讀索引可以從合併成一個單獨的大segment中收益,減少索引碎片,減少JVM堆常駐內存。強制合併索引操作會耗費大量磁盤IO,儘量配置在業務低峯期(例如凌晨)執行。歷史數據索引如果業務上不再支持查詢請求,可以考慮關閉索引,減少JVM內存佔用。
索引forcemerge API
curl -XPOST "http://localhost:9200/abc20180923/_forcemerge?max_num_segments=1"
索引關閉API
curl -XPOST "http://localhost:9200/abc2017*/_close"
04
配置合適的分詞器
Elasticsearch內置了很多分詞器,包括standard、cjk、nGram等,也可以安裝自研/開源分詞器。根據業務場景選擇合適的分詞器,避免全部採用默認standard分詞器。
常用分詞器:
standard:默認分詞,英文按空格切分,中文按照單個漢字切分。
cjk:根據二元索引對中日韓文分詞,可以保證查全率。
nGram:可以將英文按照字母切分,結合ES的短語搜索(match_phrase)使用。
IK:比較熱門的中文分詞,能按照中文語義切分,可以自定義詞典。
pinyin:可以讓用戶輸入拼音,就能查找到相關的關鍵詞。
aliws:阿里巴巴自研分詞,支持多種模型和分詞算法,詞庫豐富,分詞結果準確,適用於電商等對查準要求高的場景。
分詞效果測試API
curl -XPOST "http://localhost:9200/_analyze" -H 'Content-Type: application/json' -d'
{
"analyzer": "ik_max_word",
"text": "南京市長江大橋"
}'
常用中文分詞器效果對比
05
配置查詢聚合節點
查詢聚合節點可以發送粒子查詢請求到其他節點,收集和合並結果,以及響應發出查詢的客戶端。通過給查詢聚合節點配置更高規格的CPU和內存,可以加快查詢運算速度、提升緩存命中率。
某客戶使用25臺8核CPU32G內存節點Elasticsearch集羣,查詢QPS在4000左右。增加6臺16核CPU32G內存節點作爲查詢聚合節點,觀察服務器CPU、JVM堆內存使用情況,並調整緩存、分片、副本參數,查詢QPS達到12000。
# 查詢聚合節點配置(conf/elasticsearch.yml):
node.master:false
node.data:false
node.ingest:false
06
設置查詢讀取記錄條數和字段
默認的查詢請求通常返回排序後的前10條記錄,最多一次讀取10000條記錄,通過from和size參數控制讀取記錄範圍,避免一次讀取過多的記錄。通過_source參數可以控制返回字段信息,儘量避免讀取大字段。
查詢請求示例
curl -XGET http://localhost:9200/fulltext001/_search?pretty -H 'Content-Type: application/json' -d '
{
"from": 0,
"size": 10,
"_source": "id",
"query": {
"bool": {
"must": [
{"match": {"content":"虎嗅"}}
]
}
},
"sort": [
{
"id": {
"order": "asc"
}
}
]
}
'
07
設置teminate_after查詢快速返回
如果不需要精確統計查詢命中記錄條數,可以配teminate_after指定每個shard最多匹配N條記錄後返回,設置查詢超時時間timeout。在查詢結果中可以通過“terminated_early”字段標識是否提前結束查詢請求。
teminate_after查詢語法示例
curl -XGET "http://localhost:9200/twitter/_search" -H 'Content-Type: application/json' -d'
{
"from": 0,
"size": 10,
"timeout": "10s",
"terminate_after": 1000,
"query": {
"bool": {
"filter": {
"term": {
"user": "elastic"
}
}
}
}
}'
08
避免查詢深度翻頁
Elasticsearch默認只允許查看排序前10000條的結果,當翻頁查看排序靠後的記錄時,響應耗時一般較長。使用search_after方式查詢會更輕量級,如果每次只需要返回10條結果,則每個shard只需要返回search_after之後的10個結果即可,返回的總數據量只是和shard個數以及本次需要的個數有關,和歷史已讀取的個數無關。
search_after查詢語法示例
curl -XGET "http://localhost:9200/twitter/_search" -H 'Content-Type: application/json' -d'
{
"size": 10,
"query": {
"match": {
"message": "Elasticsearch"
}
},
"sort": [
{"_score": {"order": "desc"}},
{"_id": {"order":"asc"}}
],
"search_after": [
0.84290016, //上一次response中某個doc的score
"1024" //上一次response中某個doc的id
]
}'
09
避免前綴模糊匹配
Elasticsearch默認支持通過*?正則表達式來做模糊匹配,如果在一個數據量較大規模的索引上執行模糊匹配,尤其是前綴模糊匹配,通常耗時會比較長,甚至可能導致內存溢出。儘量避免在高併發查詢請求的生產環境執行這類操作。
某客戶需要對車牌號進行模糊查詢,通過查詢請求"車牌號:*A8848*"查詢時,往往導致整個集羣負載較高。通過對數據預處理,增加冗餘字段"車牌號.keyword",並事先將所有車牌號按照1元、2元、3元...7元分詞後存儲至該字段,字段存儲內容示例:滬,A,8,4,滬A,A8,88,84,48,滬A8...滬A88488。通過查詢"車牌號.keyword:A8848"即可解決原來的性能問題。
10
避免索引稀疏
Elasticsearch6.X之前的版本默認允許在一個index下面創建多個type,Elasticsearch6.X版本只允許創建一個type,Elasticsearch7.X版本只允許type值爲“_doc”。在一個索引下面創建多個字段不一樣的type,或者將幾百個字段不一樣的索引合併到一個索引中,會導致索引稀疏問題。
建議每個索引下只創建一個type,字段不一樣的數據分別獨立創建index,不要合併成一個大索引。每個查詢請求根據需要去讀取相應的索引,避免查詢大索引掃描全部記錄,加快查詢速度。
11
擴容集羣節點個數
升級節點規格
通常服務器節點數越多,服務器硬件配置規格越高,Elasticsearch集羣的處理能力越強。
在不同節點規模下的查詢性能測試
(測試環境:Elasticsearch5.5.3集羣,單節點16核CPU、64G內存、2T SSD盤,10億條人口戶籍登記信息,數據大小1TB, 20索引分片)
集羣節點數 | 副本數 | 10併發檢索平均響應時間 | 50併發檢索平均響應時間 | 100併發檢索平均響應時間 | 200併發檢索平均響應時間 | 200併發QPS | 200併發CPU使用率 | 200併發CPUIO等待 |
1 | 0 | 77ms | 459ms | 438ms | 1001ms | 200 | 16% | 52% |
3 | 0 | 38ms | 103ms | 162ms | 298ms | 669 | 45% | 34% |
3 | 2 | 271ms | 356ms | 577ms | 818ms | 244 | 19% | 54% |
10 | 0 | 21ms | 36ms | 48ms | 81ms | 2467 | 40% | 10% |
不同集羣節點規模寫入性能測試
(測試環境:Elasticsearch6.3.2集羣,單節點16核CPU、64G內存、2T SSD盤,10億條人口戶籍登記信息,單條記錄1KB,數據集大小1TB,20個併發寫入線程)
集羣節點數 | 副本數 | 寫入TPS | 耗時 | 集羣CPU使用率 |
10 | 0 | 88945 | 11242s | 50% |
50 | 0 | 180638 | 5535s | 20% |
在條件允許的情況下,建議可以通過實際的數據和使用場景測試出適合自己的最佳實踐。得益於阿里雲Elasticsearch提供的彈性擴容功能,阿里雲Elasticsearch用戶可以在實際使用時根據情況隨時增加磁盤大小、擴容節點個數、升級節點規格。
< END >
喜歡就點個在看 or 轉發個朋友圈唄
衣舞晨風
推薦閱讀: