阿里云Elasticsearch性能优化实践
Elasticsearch是一款流行的分布式開源搜索和數據分析引擎,具備高性能、易擴展、容錯性強等特點。它強化了Apache Lucene的搜索能力,把掌控海量數據索引和查詢的方式提升到一個新的層次。本文結合開源社區和阿里云平臺的實踐經驗,探討如何調優Elasticsearch的性能提高索引和查詢吞吐量。
- 一. Elasticsearch部署建議
1.選擇合理的硬件配置,盡可能使用SSD
? ? ? Elasticsearch最大的瓶頸往往是磁盤讀寫性能,尤其是隨機讀取性能。使用SSD(PCI-E接口SSD卡/SATA接口SSD盤)通常比機械硬盤(SATA盤/SAS盤)查詢速度快5~10倍,寫入性能提升不明顯。
? ? ? ? 對于文檔檢索類查詢性能要求較高的場景,建議考慮SSD作為存儲,同時按照1:10的比例配置內存和硬盤。對于日志分析類查詢并發要求較低的場景,可以考慮采用機械硬盤作為存儲,同時按照1:50的比例配置內存和硬盤。單節點存儲數據建議在2TB以內,最大不要超過5TB,避免查詢速度慢、系統不穩定。
? ? ? ? 在單機存儲1TB數據場景下,SATA盤和SSD盤的全文檢索性能對比(測試環境:Elasticsearch5.5.3,10億條人口戶籍登記信息,單機16核CPU、64GB內存,12塊6TB SATA盤,2塊1.5 TB SSD盤):
| 磁盤類型 | 并發數 | QPS | 平均檢索響應時間 | 50%請求響應時間 | 90%請求響應時間 | IOPS |
| SATA盤 | 10并發 | 17 | 563ms | 478ms | 994ms | 1200 |
| SATA盤 | 50并發 | 64 | 773ms | 711ms | 1155ms | 1800 |
| SATA盤 | 100并發 | 110 | 902ms | 841ms | 1225ms | 2040 |
| SATA盤 | 200并發 | 84 | 2369ms | 2335ms | 2909ms | 2400 |
| SSD盤 | 10并發 | 94 | 105ms | 90ms | 200ms | 25400 |
| SSD盤 | 50并發 | 144 | 346ms | 341ms | 411ms | 66000 |
| SSD盤 | 100并發 | 152 | 654ms | 689ms | 791ms | 60000 |
| SSD盤 | 200并發 | 210 | 950ms | 1179ms | 1369ms | 60000 |
?
? ? ? ? 2.給JVM配置機器一半的內存,但是不建議超過32G
? ? ? ? 修改conf/jvm.options配置,-Xms和-Xmx設置為相同的值,推薦設置為機器內存的一半左右,剩余一半留給操作系統緩存使用。jvm內存建議不要低于2G,否則有可能因為內存不足導致ES無法正常啟動或內存溢出,jvm建議不要超過32G,否則jvm會禁用內存對象指針壓縮技術,造成內存浪費。機器內存大于64G內存時,推薦配置-Xms30g -Xmx30g 。
?
3.規模較大的集群配置專有主節點,避免腦裂問題
? ? ? ? Elasticsearch主節點(master節點)負責集群元信息管理、index的增刪操作、節點的加入剔除,定期將最新的集群狀態廣播至各個節點。在集群規模較大時,建議配置專有主節點只負責集群管理,不存儲數據,不承擔數據讀寫壓力。
# 專有主節點配置(conf/elasticsearch.yml): node.master:true node.data: false node.ingest:false# 數據節點配置(conf/elasticsearch.yml): node.master:false node.data:true node.ingest:true? ? ? ? Elasticsearch默認每個節點既是候選主節點,又是數據節點。最小主節點數量參數minimum_master_nodes推薦配置為候選主節點數量一半以上,該配置告訴Elasticsearch當沒有足夠的master候選節點的時候,不進行master節點選舉,等master節點足夠了才進行選舉。
? ? ? ? 例如對于3節點集群,最小主節點數量從默認值1改為2。
# 最小主節點數量配置(conf/elasticsearch.yml): discovery.zen.minimum_master_nodes: 2? ? ? ? 4.Linux操作系統調優
? ? ? ? 關閉交換分區,防止內存置換降低性能。 將/etc/fstab 文件中包含swap的行注釋掉
sed -i '/swap/s/^/#/' /etc/fstab swapoff -a? ? ? ? 單用戶可以打開的最大文件數量,可以設置為官方推薦的65536或更大些
echo "* - nofile 655360" >> /etc/security/limits.conf? ? ? ? 單用戶線程數調大
echo "* - nproc 131072" >> /etc/security/limits.conf? ? ? ? 單進程可以使用的最大map內存區域數量
echo "vm.max_map_count = 655360" >> /etc/sysctl.conf? ? ? ? 參數修改立即生效
sysctl -p?
- 二.?索引性能調優建議
? ? ? ? 1.設置合理的索引分片數和副本數
? ? ? ? 索引分片數建議設置為集群節點的整數倍,初始數據導入時副本數設置為0,生產環境副本數建議設置為1(設置1個副本,集群任意1個節點宕機數據不會丟失;設置更多副本會占用更多存儲空間,操作系統緩存命中率會下降,檢索性能不一定提升)。單節點索引分片數建議不要超過3個,每個索引分片推薦10-40GB大小。索引分片數設置后不可以修改,副本數設置后可以修改。Elasticsearch6.X及之前的版本默認索引分片數為5、副本數為1,從Elasticsearch7.0開始調整為默認索引分片數為1、副本數為1。
? ? ? ? 不同分片數對寫入性能的影響(測試環境:7節點Elasticsearch6.3集群,寫入30G新聞數據,單節點56核CPU、380G內存、3TB SSD卡,0副本,20線程寫入,每批次提交10M左右數據。):?
| 集群索引分片數 | 單節點索引分片數 | 寫入耗時 |
| 2 | 0/1 | 600s |
| 7 | 1 | 327s |
| 14 | 2 | 258s |
| 21 | 3 | 211s |
| 28 | 4 | 211s |
| 56 | 8 | 214s |
? ? ? ? 索引設置:
curl -XPUT http://localhost:9200/fulltext001?pretty -H 'Content-Type: application/json' -d ' {"settings" : {"refresh_interval": "30s","merge.policy.max_merged_segment": "1000mb","translog.durability": "async","translog.flush_threshold_size": "2gb","translog.sync_interval": "100s","index" : {"number_of_shards" : "21","number_of_replicas" : "0"}} } '? ? ? ? mapping設置:
curl -XPOST http://localhost:9200/fulltext001/doc/_mapping?pretty -H 'Content-Type: application/json' -d ' {"doc" : {"_all" : {"enabled" : false},"properties" : {"content" : {"type" : "text","analyzer":"ik_max_word"},"id" : {"type" : "keyword"}}} } '? ? ? ? 寫入數據示例:
curl -XPUT 'http://localhost:9200/fulltext001/doc/1?pretty' -H 'Content-Type: application/json' -d ' {"id": "https://www.huxiu.com/article/215169.html","content": "“娃娃機,迷你KTV,VR體驗館,堪稱商場三大標配‘神器’。”一家地處商業中心的大型綜合體負責人告訴懂懂筆記,在過去的這幾個月里,幾乎所有的綜合體都“標配”了這三種“設備”…" }'? ? ? ? 修改副本數示例:
curl -XPUT "http://localhost:9200/fulltext001/_settings" -H 'Content-Type: application/json' -d' {"number_of_replicas": 1 }'? ? ? ? 2.使用批量請求
? ? ? ? 使用批量請求將產生比單文檔索引請求好得多的性能。寫入數據時調用批量提交接口,推薦每批量提交5~15MB數據。例如單條記錄1KB大小,每批次提交10000條左右記錄寫入性能較優;單條記錄5KB大小,每批次提交2000條左右記錄寫入性能較優。
? ? ? ? 批量請求接口API:
curl -XPOST "http://localhost:9200/_bulk" -H 'Content-Type: application/json' -d' { "index" : { "_index" : "test", "_type" : "_doc", "_id" : "1" } } { "field1" : "value1" } { "delete" : { "_index" : "test", "_type" : "_doc", "_id" : "2" } } { "create" : { "_index" : "test", "_type" : "_doc", "_id" : "3" } } { "field1" : "value3" } { "update" : {"_id" : "1", "_type" : "_doc", "_index" : "test"} } { "doc" : {"field2" : "value2"} } '??? ? ? ?3.通過多進程/線程發送數據
? ? ? ? 單線程批量寫入數據往往不能充分利用服務器CPU資源,可以嘗試調整寫入線程數或者在多個客戶端上同時向Elasticsearch服務器提交寫入請求。與批量調整大小請求類似,只有測試才能確定最佳的worker數量。 可以通過逐漸增加工作任務數量來測試,直到集群上的I / O或CPU飽和。
? ? ? ? 4.調大refresh interval
? ? ? ? 在 Elasticsearch 中,寫入和打開一個新段的輕量的過程叫做?refresh?。?默認情況下每個分片會每秒自動刷新一次。這就是為什么我們說 Elasticsearch 是?近?實時搜索: 文檔的變化并不是立即對搜索可見,但會在一秒之內變為可見。
? ? ? ? 并不是所有的情況都需要每秒刷新。可能你正在使用 Elasticsearch 索引大量的日志文件,你可能想優化索引速度而不是近實時搜索,可以通過設置 refresh_interval,降低每個索引的刷新頻率。
? ? ? ? 設置refresh interval API:
curl -XPUT "http://localhost:9200/index" -H 'Content-Type: application/json' -d' {"settings" : {"refresh_interval": "30s"} }'? ? ? ? refresh_interval?可以在既存索引上進行動態更新。 在生產環境中,當你正在建立一個大的新索引時,可以先關閉自動刷新,待開始使用該索引時,再把它們調回來:
curl -XPUT "http://localhost:9200/index/_settings" -H 'Content-Type: application/json' -d' { "refresh_interval": -1 }' curl -XPUT "http://localhost:9200/index/_settings" -H 'Content-Type: application/json' -d' { "refresh_interval": "1s" }'? ? ? ? 5.設計mapping配置合適的字段類型
? ? ? ? Elasticsearch在寫入文檔時,如果請求中指定的索引名不存在,會自動創建新索引,并根據文檔內容猜測可能的字段類型。但這往往不是最高效的,我們可以根據應用場景來設計合理的字段類型。
? ? ? ? 例如寫入一條記錄:
curl -XPUT "http://localhost:9200/twitter/doc/1?pretty" -H 'Content-Type: application/json' -d' {"user": "kimchy","post_date": "2009-11-15T13:12:00","message": "Trying out Elasticsearch, so far so good?" }'? ? ? ? 查詢Elasticsearch自動創建的索引mapping,會發現將post_date字段自動識別為date類型,但是message和user字段被設置為text、keyword冗余字段,造成寫入速度降低、占用更多磁盤空間。
curl -XGET "http://localhost:9200/twitter" {"twitter": {"mappings": {"doc": {"properties": {"message": {"type": "text","fields": {"keyword": {"type": "keyword","ignore_above": 256}}},"post_date": {"type": "date"},"user": {"type": "text","fields": {"keyword": {"type": "keyword","ignore_above": 256}}}}}},"settings": {"index": {"number_of_shards": "5","number_of_replicas": "1",}}} }? ? ? ? 根據業務場景設計索引配置合理的分片數、副本數,設置字段類型、分詞器。如果不需要合并全部字段,禁用_all字段,通過copy_to來合并字段。
curl -XPUT "http://localhost:9200/twitter?pretty" -H 'Content-Type: application/json' -d' {"settings" : {"index" : {"number_of_shards" : "20","number_of_replicas" : "0"}} }' curl -XPOST "http://localhost:9200/twitter/doc/_mapping?pretty" -H 'Content-Type: application/json' -d' {"doc" : {"_all" : {"enabled" : false},"properties" : {"user" : {"type" : "keyword"},"post_date" : {"type" : "date"},"message" : {"type" : "text","analyzer" : "cjk"}}} }'?
- 三.?查詢性能調優建議
? ? ? ? 1.使用過濾器緩存和分片查詢緩存
? ? ? ? 默認情況下,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"? ? ? ? 2.使用路由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}...... }? ? ? ? 3.強制合并只讀索引,關閉歷史數據索引
? ? ? ? 只讀的索引可以從合并成一個單獨的大segment中收益,減少索引碎片,減少JVM堆常駐內存。歷史數據索引如果業務上不再支持查詢請求,可以考慮關閉索引,減少JVM內存占用。
? ? ? ? 索引forcemerge API:
curl -XPOST "http://localhost:9200/abc20180923/_forcemerge"? ? ? ? 索引關閉API:
curl -XPOST "http://localhost:9200/abc2017*/_close"4.配置查詢聚合節點
? ? ? ? 查詢聚合節點可以發送粒子查詢請求到其他節點,收集和合并結果,以及響應發出查詢的客戶端。通過給查詢聚合節點配置更高規格的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?
? ? ? ? 5.設置查詢讀取記錄條數和字段
? ? ? ? 默認的查詢請求通常返回排序后的前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"}}] } '?
? ? ? 6.避免前綴模糊匹配
? ? ? ? Elasticsearch默認支持通過*?正則表達式來做模糊匹配,如果在一個數據量超過10億條的索引上執行模糊匹配,尤其是前綴模糊匹配,通常耗時會比較長,甚至可能導致內存溢出。盡量避免在高并發查詢請求的生產環境執行這類操作。
? ? ? ? 某客戶需要對車牌號進行模糊查詢,通過查詢請求"車牌號:*A8848*"查詢時,往往導致整個集群負載較高。通過對數據預處理,增加冗余字段"車牌號.keyword",并事先將所有車牌號按照1元、2元、3元...7元分詞后存儲至該字段,字段存儲內容示例:滬,A,8,4,滬A,A8,88,84,48,滬A8...滬A88488。通過查詢"車牌號.keyword:A8848"即可解決原來的性能問題。
? ? ? 7.避免索引稀疏
? ? ? ? Elasticsearch6.X之前的版本默認允許在一個index下面創建多個type,Elasticsearch6.X及之后的版本只允許創建一個type。在一個type下面創建多個字段不一樣的type,或者將幾百個字段不一樣的索引合并到一個索引中,會導致索引稀疏問題。
? ? ? ? 建議每個索引下只創建一個type,字段不一樣的數據分別獨立創建index,不要合并成一個大索引。每個查詢請求根據需要去讀取相應的索引,避免查詢大索引掃描全部記錄,加快查詢速度。
?
? ? ? 8.擴容集群節點個數、升級節點規格
? ? ? ? 通常服務器節點數越多,服務器硬件配置規格越高,Elasticsearch集群的處理能力越強。
? ? ? ? 在不同節點規模下的查詢性能測試(測試環境:Elasticsearch5.5.3集群,單節點16核CPU、64G內存、2T SSD盤,10億條人口戶籍登記信息,數據大小1TB。):
| 集群節點數 | 副本數 | 10并發檢索平均響應時間 | 50并發檢索平均響應時間 | 100并發檢索平均響應時間 | 200并發檢索平均響應時間 | 200并發QPS | 200并發CPU使用率 | 200并發CPU IO等待 |
| 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性能优化实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: RHCE培训笔记——GRUB
- 下一篇: 专门的考试学习培训课程录象资料bt下载