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,可避免脑裂问题

相关文章
相关标签/搜索