Elasticsearch 優化

Elasticsearch是一個基於Lucene的搜索服務器,其搜索的核心原理是倒排索引,今天談下在平常項目中使用它遇到的一些問題及優化解決辦法。node

一. 搜索的深度分頁問題python

在平常項目中,常常會有分頁搜索並支持跳頁的需求,相似百度、Google搜索那樣,使用ES進行這類需求的搜索時通常採用from/size的方式,from指明開始的位置,size指定獲取的條數,經過這種方式獲取數據稱爲深度分頁。api

經過這種分頁方式當我取 from爲11000,size爲10時,發現沒法獲取:服務器

ES報錯說超過了max_result_window網絡

初步解決方案:數據結構

我修改了索引的設置,將max_result_window設置爲了10000000:app

PUT ccnu_resource/_settings { "index": { "max_result_window": 10000000 } }

 這樣作雖然解決了問題,而且目前在性能上也沒有太大問題。一次當我用Google搜索時時,突發奇想,想試試Google的最大分頁數:async

 我發現Google提示:Google爲全部查詢的結果數都不會超過1000,而後我迅速嘗試了百度和微軟的BING:elasticsearch

百度只顯示76頁,當修改url時,76頁之外的也不會顯示,這時候會跳到第一頁,微軟BING只會顯示97頁,當你繼續下一頁時它會回退當前頁的前一頁,這時候我從新查閱了ES分頁遍歷相關資料,這種from/to的方式採用的是深度分頁機制,而且目前全部分佈式搜索引擎都存在深度分頁的問題。分佈式

ES深度分頁:

因爲數據是分散存儲在各個分片上的,因此ES會從每一個分片上取出當前查詢的所有數據,好比from:9990,size:10,ES會在每一個分片上取10000個document,而後聚合每一個分片的結果再排序選取前10000個document;因此當from的值愈來愈大,頁數愈來愈多時,ES處理的document就越多,同時佔用的內存也愈來愈大,因此當數據量很大、請求數不少時,搜索的效率會大大下降;因此ES默認max_result_window爲10000。

因此若是要使用from/size的方式分頁遍歷,最好使用ES默認的max_result_window,能夠根據本身的業務需求適當增長或減小max_result_window的值,可是建議以跳頁的方式分頁最好控制頁數在1000之內,max_result_window的值最好不要修改。

 

二. Mapping設置與Query查詢優化問題

在ES中建立Mappings時,默認_source是enable=true,會存儲整個document的值,當執行search操做的時,會返回整個document的信息。若是隻想返回document的部分fields,但_source會返回原始全部的內容,當某些不須要返回的field很大時,ES查詢的性能會下降,這時候能夠考慮使用store結合_source的enable=false來建立mapping。

 

PUT article_index { "mappings": { "es_article_doc":{ "_source":{ "enabled":false }, "properties":{ "title":{ "type":"text", "fields":{ "keyword":{ "type":"keyword" } }, "store":true }, "abstract":{ "type":"text", "fields":{ "keyword":{ "type":"keyword" } }, "store":true }, "content":{ "type":"text", "store":true } } } } }

 

能夠設置_source的enable:false,來單獨存儲fields,這樣查詢指定field時不會加載整個_source,經過stored_fields返回指定的fields,而且能夠對指定field作高亮顯示的需求:

GET article_index/_search { "stored_fields": [ "title" ], "query": { "match": { "content": "async" } }, "highlight": { "fields": { "content": {} } } }

 使用store在特定需求下會必定程度上提升ES的效率,可是store對於複雜的數據類型如nested類型不支持:

# nested類型 PUT article_index_nested { "mappings": { "es_article_nes_doc": { "properties": { "title": { "type": "text", "fields": { "keyword": { "type": "keyword" } } }, "comment": { "type": "nested", "properties": { "username": { "type": "keyword" }, "content": { "type": "text" } } } } } } }

添加數據:

PUT article_index_nested/es_article_nes_doc/1 { "title": "Harvard_fly 淺談 ES優化", "comments": [ { "username": "alice", "date": "2018-11-13", "content": "aaa" }, { "username": "bob", "date": "2018-11-12", "content": "bbb" } ] }

這種nested類型的store就不支持了,只能經過_source返回數據,若是須要返回指定field能夠在search中經過_source指定field:

GET article_index_nested/_search { "_source": ["title","comments"], "query": { "bool": { "must": [ { "match": { "comments.username": "alice" } }, { "match": { "comments.content": "aaa" } } ] } } }

 

三. ES讀寫優化問題

 ES讀性能的優化主要是查詢的優化,在查詢中儘可能使用filter,若是遇到查詢慢可使用explain進行慢查詢,進而優化數據模型和query;對於ES的寫優化,最好採用bulk批量插入,下面以python的api做爲例子說明:

 def bulk_insert_data(cls, qid_data_list): """ 批量插入試題到ES庫 :param qid_data_list: qid ES結構列表 :return: """ if not isinstance(qid_data_list, (list, )): raise ValueError('qid_data_list數據結構爲列表') es = connections.get_connection() index_name = cls._doc_type.index doc_type_name = cls.snake_case(cls.__name__) def gen_qid_data(): for dt in qid_data_list: yield { '_index': index_name, '_type': doc_type_name, '_id': dt['qid'], '_source': dt } bulk(es, gen_qid_data())

使用bulk批量插入數據到ES,在Python中bulk位於elasticsearch.helpers下,可是使用bulk並非一次插入的數據量越大越好,當一次插入的數據量過大時,ES的寫性能反而會下降,具體跟ES硬件配置有關,我測試的一次插入3000道試題詳情數據會比一次2000道速度慢,3000道試題詳情大約30M左右。

 若是追求寫入速度,還能夠在寫入前將replicas副本設置爲0,寫入完成後再將其設置爲正常副本數,由於ES在寫入數據時會將數據寫一份到副本中,副本數越多寫入的速度會越慢,但通常不建議將replicas副本設置爲0,由於若是在寫入數據的過程當中ES宕機了可能會形成數據丟失。

 

四. ES配置優化問題

在ES的集羣配置中,master是ES選舉出來的,在一個由node一、node二、node3組成的集羣中初始狀態node1爲主節點,node1因爲網絡問題與子節點失去聯繫,這時候ES從新選舉了node2爲主節點,當node1網絡恢復時,node1會維護本身的集羣node1爲主節點,這時候集羣中就存在node1和node2兩個主節點了,而且維護不一樣的cluster state,這樣就會形成沒法選出正確的master,這個問題就是腦裂問題。

腦裂問題的解決辦法(quorum機制):

quorum計算公式:quorum = 可選舉節點數/2 + 1

只有當可選舉節點數大於等於quorum時纔可進行master選舉,在3個可選舉節點中,quorum=3/2+1=2   在node1失去網絡響應後 node2和node3可選舉節點爲2 能夠選舉,當node1恢復後,node1只有一個節點,可選舉數爲1,小於quorum,所以避免了腦裂問題;即設置discovery.zen.minimum_master_nodes:quorum,可避免腦裂問題

相關文章
相關標籤/搜索