ElasticSearch 優化實戰
二. 查詢效能(Query Perofrmance)
王道是什麼?routing,routing,還是 routing。
我們為了提高查詢速度,減少慢查詢,結合自己的業務實踐,使用多個叢集,每個叢集使用不同的 routing。比如,使用者是一個routing維度。
在實踐中,這個routing 非常重要。
我們碰到一種情況,想把此維度的查詢(即使用者查詢)引到非使用者routing 的叢集,結果叢集完全頂不住!
在大型的本地分類網站中,城市、類目也是一個不錯的維度。我們使用這種維度進行各種搭配。然後在前端分析查詢,把各個不同查詢分別引入合適的叢集。這樣做以後,每個叢集只需要很少的機器,而且保持很小的 CPU Usage 和 LA。從而查詢速度夠快,慢查詢幾乎消滅。
分合?
分別(索引和routing)查詢和合並(索引和routing)查詢,即此分合的意思。
索引越來越大,單個 shard 也很巨大,查詢速度也越來越慢。這時候,是選擇分索引還是更多的shards?
在實踐過程中,更多的 shards 會帶來額外的索引壓力,即 IO 壓力。
我們選擇了分索引。比如按照每個大分類一個索引,或者主要的大城市一個索引。然後將他們進行合併查詢。如:http://cluster1:9200/shanghai,beijing/_search?routing=fang,自動將查詢中城市屬性且值為上海或北京的查詢,且是房類目的,引入叢集 cluster1,並且routing等於fang。
http://cluster1:9200/other/_search?routing=jinan,linyi。小城市的索引,我們使用城市做 routing,如本例中同時查詢濟南和臨沂城市。
http://cluster1:9200/_all/_search,全部城市查詢。
再如: http://cluster2:9200/fang,che/_search?routing=shanghai_qiche,shanghai_zufang,beijing_qiche,beijing_zufang。查詢上海和北京在小分類汽車、整租的資訊,那我們進行如上合併查詢。並將其引入叢集 cluster2。
使用更多的 shards?
除了有 IO 壓力,而且不能進行全部城市或全部類目查詢,因為完全頂不住。
Elastic 官方文件建議:一個 Node 最好不要多於三個 shards。
若是 "more shards”,除了增加更多的機器,是沒辦法做到這一點的。
分索引,雖然一個 Node 總的shards 還是挺多的,但是一個索引可以保持3個以內的shards。
我們使用分索引時,全量查詢是可以頂住的,雖然壓力有點兒高。
索引越來越大,資源使用也越來越多。若是要進行更細的叢集分配,大索引使用的資源成倍增加。
有什麼辦法能減小索引?顯然,建立 doc 時,把不需要的 field 去掉是一個辦法;但是,這需要對業務非常熟悉。
有啥立竿見影的辦法?
根據我們資訊的特點,內容(field:description)佔了索引的一大半,那我們就不把 description 索引進 ES,doc 小了一倍,叢集也小了一倍,所用的資源(Memory, HD or SSD, Host, snapshot儲存,還有時間)大大節省,查詢速度自然也更快。
那要查 description 怎麼辦?
上面的例項中,我們可以把查詢引入不同叢集,自然我們也可以把 description 查詢引入一個非實時(也可以實時)叢集,這主要是我們業務特點決定的,因為description查詢所佔比例非常小,使得我們可以這樣做。
被哪些查詢搞過?第一位是 Range 查詢,這貨的效能真不敢恭維。在最熱的查詢中,若是有這貨,肯定是非常痛苦的,網頁變慢,查詢速度變慢,叢集 LA 高企,嚴重的時候會導致叢集 shard 自動下線。所以,建議在最熱的查詢中避免使用 Range 查詢。
Facet 查詢,在後續版本這個被 aggregations 替代,我們大多數時候讓它在後端進行運算。
三. 其他
1)執行緒池
執行緒池我們預設使用 fixed,使用 cached 有可能控制不好。主要是比較大的分片 relocation時,會導致分片自動下線,叢集可能處於危險狀態。在叢集高壓時,若是 cached ,分片也可能自動下線。自 1.4 版本後,我們就一直 fixed,至於新版是否還存在這個問題,就沒再試驗了。
兩個原因:一是 routing王道帶來的改善,使得叢集一直低壓執行;二是使用fixed 後,已經極少遇到自動下線shard了。
我們前面說過,user 是一個非常好的維度。這個維度很重要,routing 效果非常明顯。其他維度,需要根據業務特點,進行組合。
所以我們的叢集一直是低壓執行,就很少再去關注新版本的 使用 cached 配置問題。
hreadpool.search.queue_size 這個配置是很重要的,一般預設是夠用了,可以嘗試提高。
2)優化
每天優化是有好處的,可以大大改善查詢效能。max_num_segments 建議配置為1。雖然優化時間會變長,但是在高峰期前能完成的話,會對查詢效能有很大好處。
3) JVM GC的選擇:選擇 G1還是 CMS?
應該大多數人還是選擇了 CMS,我們使用的經驗是 G1 和 CMS 比較接近;但和 CMS 相比,還是有一點距離,至少在我們使用經驗中是如此。
JVM 32G 現象?
128G記憶體的機器配置一個 JVM,然後是巨大的 heapsize (如64G)?
還是配多個 JVM instance,較小的 heapsize(如32G)?
我的建議是後者。實際使用中,後者也能幫助我們節省不少資源,並提供不錯的效能。具體請參閱 “Don’t Cross 32 GB!" (https://www.elastic.co/guide/en/elasticsearch/guide/current/heap-sizing.html#compressed_oops)
跨 32G 時,有一個現象,使用更多的記憶體,比如 40G,效果還不如31G!
這篇文件值得大家仔細閱讀。
JVM 還有一個配置 bootstrap.mlockall: true,比較重要。這是讓 JVM 啟動的時候就 鎖定 heap 記憶體。
有沒有用過 較小的 heapsize,加上SSD?我聽說有人使用過,效果還不錯,當然,我們自己還沒試過。
4)外掛工具
推薦 kopf,是一個挺不錯的工具,更新及時,功能完備,可以讓你忘掉很多 API :)。
索引,查詢,和一些重要的配置,是今天分享的重點。
Q&A
Q1:您建議生產環境JVM採用什麼樣的引數設定?FULL GC頻率和時間如何?
CMS 標準配置。
ES_HEAP_NEWSIZE=?G
JAVA_OPTS="$JAVA_OPTS -XX:+UseCondCardMark"
JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=250"
JAVA_OPTS="$JAVA_OPTS -XX:+UseParNewGC"
JAVA_OPTS="$JAVA_OPTS -XX:+UseConcMarkSweepGC"
JAVA_OPTS="$JAVA_OPTS -XX:CMSInitiatingOccupancyFraction=75"
JAVA_OPTS="$JAVA_OPTS -XX:+UseCMSInitiatingOccupancyOnly"
Full GC 很少去care 它了。我們使用 Elasticsearch 在JVM上花的時間很少。
Q2:生產環境伺服器如何配置價效比較高?單機CPU核數、主頻?記憶體容量?磁碟容量?
記憶體大一些,CPU 多核是必要的,JVM 和 Elasticsearch 會充分使用記憶體和多核的。 關於記憶體容量的問題,很多是 JVM Tunning 的問題。 磁碟容量沒啥要求。
Q3: 分組統計(Facet 查詢或 aggregations )大多數時候讓它在後端進行運算,怎麼實現?應用如果需要實時進行統計而且併發量較大,如何優化?
因為我們是網站系統,所以對於 Facet 請求,引導到後端慢慢計算,前端初始的時候可能沒資料,但是此後就會有了。
如果是精確要求的話,那就只能從 提高 facet 查詢效能去下手,比如 routing、filter、cache、更多的記憶體...
Q4:存進Elasticsearch的資料,timestamp是UTC時間,Elasticsearch叢集會在UTC 0點,也就是北京時間早上8點自動執行優化?如何改引數設定這個時間?
我們沒有使用Elasticsearch的自動優化設定。自己控制優化時間。
Q5:我的Java程式,log4j2 Flume appender,然後機器上的Flume agent ,直接Elasticsearch 的sink avro到 es節點上,多少個agent 連在單個Elasticsearch節點比較合適 ?
ElasticSearch本身是一個分散式計算叢集,所以,請求平均分配到每個 node 即可。
Q6:我程式碼裡直接用 Java API 生成Flume appender 格式,Flume agent 裡interceptor去拆分幾個欄位,這樣是不是太累了?比較推薦的做法是不是還是各業務點自己控制欄位,呼叫Elasticsearch API 生成索引內容?
業務點自己控制生成的文件吧?如果需要產生不同routing,並且分了索引,這些其實是業務相關的。routing和不同索引,都是根據業務情況哪些查詢比較集中而進行處理的。
Q7:您見過或管理過的生產環境的Elasticsearch資料量多大?
我們使用 Elasticsearch 進行某些業務處理,資料量過億。
Q8:SSD效能提升多少?
SSD 對索引幫助非常大,效果噹噹的,提高几十倍應該是沒問題。不過,我們沒有試過完全使用SSD頂查詢,而是使用記憶體,記憶體價效比還是不錯的。
Q9:我們現在有256個shard,用uid做routing,所有查詢都是走routing。每個shard有30多G,每次擴容很慢,有什麼建議?
可以考慮使用分合查詢嗎? 或者使用更多的維度? 256個 shard 確實比較難以控制。但是如果是分索引和查詢,比more shards(256) 效果應該會好不少。
Q10:Elasticsearch排序等聚合類的操作需要用到fielddata,查詢時很慢。新版本中doc values聚合查詢操作效能提升很大,你們有沒有用過?
Facet 查詢需要更大的記憶體,更多的 CPU 資源。可以考慮routing、filter、cache等多種方式提高效能。
Aggs 將來是要替換 Facet,建議儘快替換原來的facet API。
Q11:Elasticsearch配置bootstrap.mlockall,我們在使用中發現會導致啟動很慢,因為Elasticsearch要獲取到足夠的記憶體才開始啟動。
啟動慢是可以接受的,啟動慢的原因也許是記憶體沒有有效釋放過,比如檔案 cached了。 記憶體充足的情況下,啟動速度還是蠻快的,可以接受。 JVM 和 Lucene 都需要記憶體,一般是JVM 50%, 剩下的50% 檔案cached 為Lucene 使用。
Q12:優化是一個開銷比較大的操作,每天優化的時候是否會導致查詢不可用?如何優化這塊?
優化是開銷很大的。不會導致查詢不可用。優化是值得的,大量的碎片會導致查詢效能大大降低。 如果非常 care 查詢,可以考慮多個叢集。在優化時,查詢 skip 這個叢集就可以。
Q13:Elasticsearch適合做到10億級資料查詢,每天千萬級的資料實時寫入或更新嗎?
10億是可以做到的,如果文件輕量,10億所佔的資源還不是很多。
ELK 使用 Elasticsearch ,進行日誌處理每天千萬是小case吧?
不過我們除了使用 ELK 進行日誌處理,還進行業務處理,10億級快速查詢是可以做到,不過,需要做一些工作,比如索引和shards的分分合合:)
Q14:Elasticsearch相比Solr有什麼優勢嗎?
我們當年使用 Solr 的時候,Elasticsearch 剛出來。他們都是基於 Lucene的。 Elasticsearch 相對於 solr ,省事是一個優點。而且現在 Elasticsearch 相關的應用軟體也越來越多。Solr 和 Lucene 整合度很高,更新版本是和Lucene一起的,這是個優點。
很多年沒用 Solr了,畢竟那時候資料量還不大,所以折騰的就少了,主要還是折騰 JVM。所以,就不再過多的比較了。
Q15:分詞用的什麼元件?Elasticsearch自帶的嗎?
我們使用 IK 分詞,不過其他分詞也不錯。IK分詞更新還是很及時的。而且它可以遠端更新詞典。:)
Q16: reindex有沒有好的方法?
reindex 這個和 Lucene 有關,它的 update 就是 delete+ add。
Q17:以上面的兩個例子為例 : 是儲存多份同樣的資料麼?
是兩個叢集。第一個叢集使用大城市分索引,不過,還有大部分小城市合併一個索引。大城市還是用類目進行routing,小城市合併的索引就使用城市進行routing 。
第二個叢集,大類分得索引,比如fang、che,房屋和車輛和其他類目在一個叢集上,他們使用 city+二級類目做routing。
Q18:叢集部署有沒有使用 Docker ? 我們使用的時候 ,同一個伺服器 節點之間的互相發現沒有問題 ,但是跨機器的時候需要強制指定network.publish_host 和discovery.zen.ping.unicast.hosts 才能解決叢集互相發現問題。
我們使用puppet進行部署。暫沒使用 Docker。 強制指定network.publish_host 和discovery.zen.ping.unicast.hosts 才能解決叢集,跨IP段的時候是有這個需要。
Q19:您建議採用什麼樣的資料匯流排架構來保證業務資料按routing寫入多個Elasticsearch叢集,怎麼保證多叢集Elasticsearch中的資料與資料庫中資料的一致性?
我們以前使用 php在web程式碼中進行索引和分析 query,然後引導到不同叢集。 現在我們開發了一套go rest系統——4sea,使用 redis + elastic 以綜合提高效能。
索引時,更新db的同時,提交一個文件 ID 通知4sea 進行更新,然後根據配置更新到不同叢集。
資料提交到查詢時,就是分析 query 並引導到不同叢集。
這套 4sea 系統,有機會的可以考慮開源,不算很複雜的。
Q20: 能介紹一下Elasticsearch的叢集rebanlance、段合併相關的原理和經驗嗎?
“段”合併?,我們是根據業務特點,產生幾個不一樣的叢集,主要還是 routing 不一樣。
shards 比較平均很重要的,所以選擇routing 維度是難點,選擇城市的話,大城市所在分片會非常大,此時可以考慮 分索引,幾個大城市幾個索引,然後小城市合併一個索引。
如果 shards 大小分佈平均的話,就不關心如何 allocation 了。
Q21:關於叢集rebalance,其實就是cluster.routing.allocation配置下的那些rebalance相關的設定,比如allow_rebalance/cluster_concurrent_rebalance/node_initial_primaries_recoveries,推薦怎麼配置?
分片多的情況下,這個才是需要的吧。
分片比較少時,allow_rebalance disable,然後手動也可以接受的。
分片多,一般情況會自動平衡。我們對主從不太關心。只是如果一臺機器多個 JVM instance (多個 Elasticsearch node)的話,我們寫了個指令碼來避免同一shard 在一臺機器上。
cluster_concurrent_rebalance 在恢復的時候根據情況修改。正常情況下,再改成預設就好了。
node_initial_primaries_recoveries,在保證叢集低壓的情況下,不怎麼care。
kopf 上面有好多這種配置,你可以多試試。
Q22:合併查詢是非同步請求還是同步請求?做快取嗎?
合併查詢是 Elasticsearch 自帶 API。
Q23:用httpurlconnection請求的時候,會發現返回請求很耗時,一般怎麼處理?
儘可能減少慢查詢吧?我們很多工作就是想辦法如何減少慢查詢,routing和分分合合,就是這個目的。
Q24:生產環境單個節點儲存多少G資料?
有大的,有小的。小的也幾十G了。不過根據我們自己的業務特點,某些叢集就去掉了全文索引。唯一的全文索引,使用基本的routing(比較平衡的routing,比如user。城市的話,就做不到平衡了,因為大城市資料很多),然後做了 快照,反正是增量快照,1小時甚至更短時間都可以考慮!!!去掉全文索引的其他業務叢集,就小多了。