当前位置: 首页 > news >正文

Elasticsearch 优化

Elasticsearch是一个基于Lucene的搜索服务器,其搜索的核心原理是倒排索引,今天谈下在日常项目中使用它遇到的一些问题及优化解决办法。

一. 搜索的深度分页问题

在日常项目中,经常会有分页搜索并支持跳页的需求,类似百度、Google搜索那样,使用ES进行这类需求的搜索时一般采用from/size的方式,from指明开始的位置,size指定获取的条数,通过这种方式获取数据称为深度分页。

通过这种分页方式当我取 from为11000,size为10时,发现无法获取:

ES报错说超过了max_result_window

初步解决方案:

我修改了索引的设置,将max_result_window设置为了10000000:

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

 这样做虽然解决了问题,并且目前在性能上也没有太大问题。一次当我用Google搜索时时,突发奇想,想试试Google的最大分页数:

 我发现Google提示:Google为所有查询的结果数都不会超过1000,然后我迅速尝试了百度和微软的BING:

百度只显示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选举出来的,在一个由node1、node2、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,可避免脑裂问题

转载于:https://www.cnblogs.com/FG123/p/9934244.html

相关文章:

  • 深入理解多线程(三)—— Java的对象头
  • 内存池原理大揭秘
  • Python3爬取英雄联盟英雄皮肤大图
  • (6)添加vue-cookie
  • win10下配置java jdk jre环境变量
  • 如何写一个日志采集工具
  • ubuntu linux下解决“no java virtual machine was found after searching the following locations:”的方法...
  • apollo-server 返回模拟数据
  • HTTP--网络协议分层,http历史(二)
  • Java编写基于netty的RPC框架
  • 使用python编写游戏修改器
  • 通过Eclipse 为java 项目生成Api 文档、JavaDoc
  • ios多个target
  • php去除html标签
  • 11月17日站立会议
  • IDEA常用插件整理
  • java 多线程基础, 我觉得还是有必要看看的
  • Java教程_软件开发基础
  • Java-详解HashMap
  • MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇
  • Tornado学习笔记(1)
  • yii2中session跨域名的问题
  • 阿里云应用高可用服务公测发布
  • 初识 beanstalkd
  • 对象管理器(defineProperty)学习笔记
  • 前端攻城师
  • 如何用vue打造一个移动端音乐播放器
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  • Spring Batch JSON 支持
  • 阿里云服务器如何修改远程端口?
  • 进程与线程(三)——进程/线程间通信
  • ​云纳万物 · 数皆有言|2021 七牛云战略发布会启幕,邀您赴约
  • # centos7下FFmpeg环境部署记录
  • (第27天)Oracle 数据泵转换分区表
  • (第61天)多租户架构(CDB/PDB)
  • (二十一)devops持续集成开发——使用jenkins的Docker Pipeline插件完成docker项目的pipeline流水线发布
  • (附源码)springboot家庭财务分析系统 毕业设计641323
  • (正则)提取页面里的img标签
  • (转)编辑寄语:因为爱心,所以美丽
  • (转)可以带来幸福的一本书
  • (转载)在C#用WM_COPYDATA消息来实现两个进程之间传递数据
  • ./configure,make,make install的作用(转)
  • .NET Compact Framework 多线程环境下的UI异步刷新
  • .NET delegate 委托 、 Event 事件
  • .NET 设计模式—简单工厂(Simple Factory Pattern)
  • .NET/C# 项目如何优雅地设置条件编译符号?
  • @ 代码随想录算法训练营第8周(C语言)|Day53(动态规划)
  • @hook扩展分析
  • [ IO.File ] FileSystemWatcher
  • [04]Web前端进阶—JS伪数组
  • [100天算法】-目标和(day 79)
  • [20150707]外部表与rowid.txt
  • [20160902]rm -rf的惨案.txt
  • [20161101]rman备份与数据文件变化7.txt
  • [bbk5179]第66集 第7章 - 数据库的维护 03