值得擁有的 ES 讀場景、寫場景性能優化指南
本文轉載自微信公眾號「小姐姐味道」,作者小姐姐養的狗 。轉載本文請聯系小姐姐味道公眾號。
ES作為NoSQL數據庫里非常重要的一員,使用越來越廣泛。雖然它因為索引延遲的原因,數據在時效性上有一些缺陷,但其大容量、分布式的優秀設計,使得它在時效性要求并不是特別高的類實時搜索領域,能夠大展身手。
根據使用場景和用途,ES可以分為寫入和讀取兩種典型的應用方式。比如ELKB,我們就需要額外關注它的寫優化;再比如從MySQL中同步數據到ES的寬表,我們就需要額外關注它的讀優化。
廢話不多說,我們直接show一下優化方法。如果你對ES的一些概念還不是很清楚,建議收藏本文慢慢看。
1.寫入優化
日志屬于寫多讀少的業務場景,對寫入速度要求很高。拿我們其中一個集群來說,單集群日志量達到百TB,每秒鐘日志寫入量達到10W條。
數據寫入,主要有三個動作:flush、refresh和merge。通過調整它們的行為,即可在性能和數據可靠性之間進行權衡。
1.1 translog異步化
首先,ES需要寫一份translog,它類似于MySQL中的redolog,為的是避免在斷電的時候數據丟失。ES默認每次請求都進行一次flush,但對于日志來說,這沒有必要,可以將這個過程改為異步的,刷盤間隔為60秒。參數如下:
- curl-H"Content-Type: application/json"-XPUT'http://localhost:9200/_all/_settings?preserve_existing=true'-d'{
- "index.translog.durability" : "async",
- "index.translog.flush_threshold_size" : "512mb",
- "index.translog.sync_interval" : "60s"
- }'
這可以說是最重要的一步優化了,對性能的影響最大,但在極端情況下會有丟失部分數據的可能。對于日志系統來說,是可以忍受的。
1.2 增加refresh間隔
除了寫translog,ES還會將數據寫入到一個緩沖區中。但是注意了!此時,緩沖區的內容是無法被搜索到的,它還需要寫入到segment里面才可以,也就是刷新到lucence索引里面。這就是refresh動作,默認1秒。也就是你寫入的數據,大概率1秒之后才會被搜索到。
這也是為什么ES不是實時搜索系統的原因,它從數據寫入到數據讀出,一般是有一個合并過程的,有一定的時間差。
通過index.refresh_interval可以修改這個刷新間隔。
對于日志系統來說,當然要把它調大一點啦。xjjdog這里調整到了120s,減少了這些落到segment的頻率,I/O的壓力自然會小,寫入速度自然會快。
- curl-H"Content-Type: application/json"-XPUT'http://localhost:9200/_all/_settings?preserve_existing=true'-d'{
- "index.refresh_interval" : "120s"
- }'
1.3 merge
merge其實是lucene的機制,它主要是合并小的segment塊,生成更大的segment,來提高檢索的速度。
原因就是refresh過程會生成一大堆小segment文件,數據刪除也會產生空間碎片。所以merge,通俗來講就像是碎片整理進程。像postgresql等,也有vaccum進程在干同樣的事。
顯而易見,這種整理操作,既浪費I/O,又浪費CPU。
如果你的系統merge非常頻繁,那么調整merge的塊大小和頻率,是一個比較好的方法。
2.讀取優化
2.1 指定路由
如果你向ES里寫數據,那么它會為你設置一個離散的隱藏ID,落到哪個分片,是不一定的。如果你根據一個查詢條件查詢數據,你設置了6個shards的話,它要查詢6次才行。如果能夠在路由的時候就知道數據在哪個分片上,查詢速度自然會上升,這就要求我們在構造數據的時候,人工指定路由規則。它的實際運行規則如下:
- shard = hash(routing) % number_of_primary_shards
比如,一個查詢會變成這樣。
- GET my-index-000001/_search
- {
- "query": {
- "terms": {
- "_routing": [ "user1" ]
- }
- }
- }
當然,如果你的查詢維度較多,又對數據的查詢速度有非常高的有求,根據routing存放多份數據是一個比較好的選擇。
2.2 rollover冷熱分離
rollover根據索引大小,文檔數或使用期限自動過渡到新索引。當rollover觸發后,將創建新索引,寫別名將更新為指向新索引,所有后續更新都將寫入新索引,比如indexname-000001.這種模式。
從rollover這個名字可以看出來,它和Java的log日志有一定的相似之處,比如Log4j的RollingFileAppender。
當索引變的非常大,通常是幾十GB,那它的查詢效率將變的非常的低,索引重建的成本也較大。實際上,很多索引的數據在時間維度上有較為明顯的規律,一些冷數據將很少被用到。這個時候,建立滾動索引將是一個比較好的辦法。
滾動索引一般可以與索引模板結合使用,實現按一定條件自動創建索引,ES的官方文檔有具體的_rollover建立方法。
2.3 使用BoolQuery替代TermQuery
Bool查詢現在包括四種子句,must、filter、should和must_not。Bool查詢是true、false對比,而TermQuery是精確的字符串比對,所以如果需求相似,BoolQuery自然會快于TermQuery。
2.4 將大查詢拆成分段查詢
有些業務的查詢比較復雜,我們不得不拼接一張非常大的寬表放在ES中,這有兩個比較明顯的問題。
- 寬表的數據往往需要從其他數據源中回查拼接而成,數據更新時對源庫或者ES本身都有較大的壓力
- 業務的查詢JSON需要書寫的非常復雜,查詢效率未知,一次查詢鎖定的內存過高,無法進行深入優化
其實,寬表不論在RDBMS中還是ES中,都會與復雜的查詢語句有關,其鎖定時間都較長,業務也不夠靈活。
應對這種場景的策略,通常將復雜的數據查詢,轉移到業務代碼的拼接上來。比如,將一段非常冗長的單條查詢,拆分成循環遍歷的100條小查詢。所有的數據庫都對較小的查詢請求有較好的響應,其整體性能整體上將優于復雜的單條查詢。
這對我們的ES索引建模能力和編碼能力提出了挑戰。畢竟,在ES層面,互不相關的幾個索引,將作為整體為其他服務提供所謂的數據中臺接口。
2.5 增加第一次索引的速度
很多業務的索引數據往往來自于MySQL等傳統數據庫,第一次索引往往是全量索引,后面才是增量索引。必要的時候,也會進行索引的重建,大量的數據灌入造成了ES的索引速度建立緩慢。
為了緩解這種情況,建議在創建索引的時候,把副本數量設置成1,即沒有從副本。等所有數據索引完畢,再將副本數量增加到正常水平。
這樣,數據能夠快速索引,副本會在后臺慢慢復制。
3.通用優化
當然,我們還可以針對ES做一些通用的優化。比如,使用監控接口或者trace工具,發現線程池有明顯的瓶頸,則需要調整線程池的大小。
具體的優化項如下。
3.1 線程池優化
新版本對線程池的配置進行了優化,不需要配置復雜的search、bulk、index線程池。有需要配置下面幾個就行了:thread_pool.get.size, thread_pool.write.size, thread_pool.listener.size, thread_pool.analyze.size。具體可觀測_cat/thread_pool接口暴露的數據進行調整。
3.2 物理冷熱分離
上面的rollover接口,我們可以實現索引滾動。但是如何將冷數據存放在比較慢但是便宜的節點上?如何將某些索引移動過去?
ES支持給節點打標簽,具體方式是在elasticsearch.yml文件中增加一些屬性。比如:
- //熱節點
- node.attr.temperature: hot
- //冷節點
- node.attr.temperature: cold
節點有了冷熱屬性后,接下來就是指定數據的冷熱屬性,來設置和調整數據分布。ES提供了index shard filtering功能來實現索引的遷移。
首先,可以對索引也設置冷熱屬性。
- PUT hot_data_index/_settings
- {
- "index.routing.allocation.require.temperature": "hot"
- }
這些索引將自動轉移到冷設備上。我們可以寫一些定時任務,通過_cat接口的數據,自動的完成這個轉移過程。
3.2 多磁盤分散I/O
其實,可以通過配置多塊磁盤的方式,來分散I/O的壓力,但容易會造成數據熱點集中在單塊磁盤上。
ES支持在一臺機器上配置多塊磁盤,所以在存儲規模上有更大的伸縮性。在配置文件中,配置path.data屬性,即可掛載多塊磁盤。
- path.data : /data1, /data2, /data3
值得注意的是,如果你是在擴容,那么就需要配合reroute接口進行索引的重新分配。
3.3 減少單條記錄的大小
Lucene的索引建立過程,非常耗費CPU,可以減少倒排索引的數量來減少CPU的損耗。第一個優化就是減少字段的數量;第二個優化就是減少索引字段的數量。具體的操作,是將不需要搜索的字段,index屬性設置為not_analyzed或者no。至于_source和_all,在實際調試中效果不大,不再贅述。
ES的使用越來越廣泛,從ELKB到APM,從NoSQL到搜索引擎,ES在企業中的地位也越來越重要。本文通過分析ES寫入和讀取場景的優化,力求從原理到實踐層面,助你為ES加速。希望你在使用ES時能夠更加得心應手。
通常,一個ES集群對配置的要求是較高的,尤其是APM等場景,甚至會占到PaaS平臺的1/3資源甚至更多。ES提供了較多的配置選項,我們可以根據應用場景,調整ES的表現,使其更好的為我們服務。
作者簡介:小姐姐味道 (xjjdog),一個不允許程序員走彎路的公眾號。聚焦基礎架構和Linux。十年架構,日百億流量,與你探討高并發世界,給你不一樣的味道。