如何提高ElasticSearch 索引速度
來自: http://www.jianshu.com/p/5eeeeb4375d4
我Google了下,大致給出的答案如下:
- 使用bulk API
- 初次索引的時候,把 replica 設置為 0
- 增大 threadpool.index.queue_size
- 增大 indices.memory.index_buffer_size
- 增大 index.translog.flush_threshold_ops
- 增大 index.translog.sync_interval
- 增大 index.engine.robin.refresh_interval
其中 5,6 屬于 TransLog 相關。
4 則和Lucene相關
3 則因為ES里大量采用線程池,構建索引的時候,是有單獨的線程池做處理的
7 的話個人認為影響不大
2 的話,能夠使用上的場景有限。個人認為Replica這塊可以使用Kafka的ISR機制。所有數據還是都從Primary寫和讀。Replica盡量只作為備份數據。
Translog
為什么要有Translog? 因為Translog順序寫日志比構建索引更高效。我們不可能每加一條記錄就Commit一次,這樣會有大量的文件和磁盤IO產生。但是我們又想避免程序掛掉或者硬件故障而出現數據丟失,所以有了Translog,通常這種日志我們叫做Write Ahead Log。
為了保證數據的完整性,ES默認是每次request結束后都會進行一次sync操作。具體可以查看如下方法:
org.elasticsearch.action.bulk.TransportShardBulkAction.processAfter
該方法會調用IndexShard.sync 方法進行文件落地。
你也可以通過設置 index.translog.durability=async 來完成異步落地。這里的異步其實可能會有一點點誤導。前面是每次request結束后都會進行sync,這里的sync僅僅是將Translog落地。而無論你是否設置了async,都會執行如下操作:
根據條件,主要是每隔sync_interval(5s) ,如果flush_threshold_ops(Integer.MAX_VALUE),flush_threshold_size(512m),flush_threshold_period(30m) 滿足對應的條件,則進行flush操作,這里除了對Translog進行Commit以外,也對索引進行了Commit。
所以如果你是海量的日志,可以容忍發生故障時丟失一定的數據,那么完全可以設置, index.translog.durability=async ,并且將前面提到的flush*相關的參數調大。
而極端情況,你還可以有兩個選擇:
-
設置 index.translog.durability=async ,接著設置 index.translog.disable_flush=true 進行禁用定時flush。然后你可以通過應用程序自己手動來控制flush。
-
通過改寫ES 去掉Translog日志相關的功能
Version
Version可以讓ES實現并發修改,但是帶來的性能影響也是極大的,這里主要有兩塊:
- 需要訪問索引里的版本號,觸發磁盤讀寫
- 鎖機制
目前而言,似乎沒有辦法直接關閉Version機制。你可以使用自增長ID并且在構建索引時,index 類型設置為create。這樣可以跳過版本檢查。
這個場景主要應用于不可變日志導入,隨著ES被越來越多的用來做日志分析,日志沒有主鍵ID,所以使用自增ID是合適的,并且不會進行更新,使用一個固定的版本號也是合適的。而不可變日志往往是追求吞吐量。
當然,如果有必要,我們也可以通過改寫ES相關代碼,禁用版本管理。
分發代理
ES是對索引進行了分片(Shard),然后數據被分發到不同的Shard。這樣 查詢和構建索引其實都存在一個問題:
如果是構建索引,則需要對數據分揀,然后根據Shard分布分發到不同的Node節點上。如果是查詢,則對外提供的Node需要收集各個Shard的數據做Merge
這都會對對外提供的節點造成較大的壓力。 解決這個有效的方式,如果你使用類似Spark Streaming這種流式處理程序,在最后往ES輸出的時候,根據將所有的數據實現按照shard進行分區(Partition),然后得到 partition -> shardId 的映射關系,直接通過RPC 方式,類似 transportService.sendRequest 將數據批量發送到發送到對應包含有對應ShardId的Node節點上。
這樣有三點好處:
- 所有的數據都被直接分到各個Node上直接處理。避免所有的數據先集中到一臺服務器
- 避免二次分發,減少一次網絡IO
- 防止最先處理數據的Node壓力太大而導致木桶短板效應
當然,還有一種辦法是,直接面向客戶提供構建索引和查詢API的Node節點都采用client模式,不存儲數據,可以達到一定的優化效果。
一些題外話
其實如果對接Spark Streaming時,完全可以禁止自動flush操作,每個batch 手動提交,這樣,一個batch周期,對應可能產生可預期的Segment文件,通過控制batch的周期和大小,甚至可以預判出ES Segment索引文件的生成大小和Merge情況。
場景
因為我正好要做日志分析類的應用,追求高吞吐量,這樣上面的三個優化其實都可以做了。
</div>