Bootstrap

Elasticsearch

Elasticsearch

一、Elasticsearch简介

​ Elasticsearch是用Java开发的一个基于Lucene的搜索服务器,它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

​ Lucene 可以说是当下最先进、高性能、全功能的搜索引擎库——无论是开源还是私有,但它也仅仅只是一个库。为了充分发挥其功能,需要使用 Java 并将 Lucene 直接集成到应用程序中。 因为Lucene 非常复杂,所以可能需要获得信息检索学位才能了解其工作原理。
​ 为了解决Lucene使用时的繁复性,于是Elasticsearch便应运而生。它使用 Java 编写,内部采用 Lucene 做索引与搜索,但是它的目标是使全文检索变得更简单,简单来说,就是对Lucene 做了一层封装,它提供了一套简单一致的 RESTful API 来帮助我们实现存储和检索。
​ 当然,Elasticsearch 不仅仅是 Lucene,并且也不仅仅只是一个全文搜索引擎。 它可以被下面这样准确地形容:

  • 一个分布式的实时文档存储,每个字段可以被索引与搜索;
  • 一个分布式实时分析搜索引擎;
  • 能胜任上百个服务节点的扩展,并支持 PB 级别的结构化或者非结构化数据。

​ ElasticSearch默认是单点运行,但由于单节点性能有限,故需要集群方式运行,实现负载均衡高可用。ElasticSearch的RESTFul API不开启用户认证,任何人通过暴露的9200端口即可以直接添加、访问、修改、删除其中数据,存在很大的安全隐患。所以我们部署时一般需要单独部署一个子接口网段用于ElasticSearch集群间通信。

官网:https://www.elastic.co/guide/index.html

中文指南:https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html

二、相关概念

1、cluster & node

​ Elasticsearch 本质上是一个分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个Elasticsearch实例。单个Elasticsearch实例称为一个节点(Node),一组节点构成一个集群(Cluster)。

​ 集群中有多个节点,其中有一个为主节点,这个主节点是可以通过选举产生的,主从节点是对于集群内部来说的。es的一个概念就是去中心化,字面上理解就是无中心节点,这是对于集群外部来说的,因为从外部来看es集群,在逻辑上是个整体,你与任何一个节点的通信和与整个es集群通信是等价的。

2、index

​ Elasticsearch 数据管理的顶层单位就叫做 Index(索引),相当于关系型数据库里的数据库的概念。另外,每个Index的名字必须是小写。

3、Document

​ Index里面单条的记录称为 Document(文档)。许多条 Document 构成了一个 Index。Document 使用 JSON 格式表示。同一个 Index 里面的 Document,不要求有相同的结构(scheme),但是最好保持相同,这样有利于提高搜索效率。

4、type

​ Document 可以分组,比如employee这个 Index 里面,可以按部门分组,也可以按职级分组。这种分组就叫做 Type(类型),它是虚拟的逻辑分组,用来过滤 Document,类似关系型数据库中的数据表。
  不同的 Type 应该有相似的结构(Schema),性质完全不同的数据(比如 products 和 logs)应该存成两个 Index,而不是一个 Index 里面的两个 Type(虽然可以做到)

5、Document metadata

​ 代表文档元数据。文档元数据为_index, _type, _id, 这三者可以唯一表示一个文档, _index表示文档在哪存放, _type表示文档的对象类别, _id为文档的唯一标识。

6、Fields

​ 代表字段。每个Document都类似一个JSON结构,它包含了许多字段,每个字段都有其对应的值,多个字段组成了一个 Document,可以类比关系型数据库数据表中的字段。
 在 Elasticsearch 中,文档(Document)归属于一种类型(Type),而这些类型存在于索引(Index)中。

7、shards

​ 代表索引分片,es可以把一个完整的索引分成多个分片,这样的好处是可以把一个大的索引拆分成多个,分布到不同的节点上。构成分布式搜索。分片的数量只能在索引创建前指定,并且索引创建后不能更改。

分片之所以重要,主要有两方面的原因:

  • 允许你水平分割/扩展你的内容容量

  • 允许你在分片(位于多个节点上)之上进行分布式的、并行的操作,进而提高性能/吞吐量

8、replicas

​ 代表索引副本,es可以设置多个索引的副本,副本的作用一是提高系统的容错性,当某个节点某个分片损坏或丢失时可以从副本中恢复。二是提高es的查询效率,es会自动对搜索请求进行负载均衡。

9、recovery

​ 代表数据恢复或叫数据重新分布,es在有节点加入或退出时会根据机器的负载对索引分片进行重新分配,挂掉的节点重新启动时也会进行数据恢复。

10、river

​ 代表es的一个数据源,也是其它存储方式(如:数据库)同步数据到es的一个方法。它是以插件方式存在的一个es服务,通过读取river中的数据并把它索引到es中。

11、gateway

​ 代表es索引快照的存储方式,es默认是先把索引存放到内存中,当内存满了时再持久化到本地硬盘。gateway对索引快照进行存储,当这个es集群关闭再重新启动时就会从gateway中读取索引备份数据。es支持多种类型的gateway,有本地文件系统(默认),分布式文件系统和Hadoop的HDFS。

12、discovery.zen

​ 代表es的自动发现节点机制,es是一个基于p2p的系统,它先通过广播寻找存在的节点,再通过多播协议来进行节点之间的通信,同时也支持点对点的交互。

13、Transport

​ 代表es内部节点或集群与客户端的交互方式,默认内部是使用tcp协议进行交互,同时它支持http协议(json格式)、thrift、servlet、memcached、zeroMQ等的传输协议(通过插件方式集成)。

三、ES安装部署

1、单节点部署
  • 下载

    下载地址:https://www.elastic.co/downloads/elasticsearch

  • 修改内存参数

    sysctl -a通过grep过滤查看参数

    #临时修改
    # sysctl -w vm.max_map_count=262144
    #重启生效
    # vim /etc/sysctl.conf
    #禁用内存与磁盘交换
    vm.swappiness=1
    #设置虚拟机内存大小
    vm.max_map_count=262144
    

    执行 sysctl -p 使用配置生效

  • 解压

  • 创建用户并修改权限

    # useradd esuser
    # chown -R esuser.esuser ./elasticsearch
    
  • 配置环境变量

    # vim /etc/pro
    JAVA_HOME=/opt/jdk1.8.0_151
    PATH=$JAVA_HOME/bin:$PATH:$HOME/.local/bin:$HOME/bin
    export PATH
    
  • 修改配置文件elasticsearch.yml

    # 集群的名字,需要自定义,如果是想要搭建es集群,则保持此项相同即可
     cluster.name: elap
    # 节点名字,需要自定义,如果想要搭建es集群,则保证此项不同即可
     node.name: es1
    # 是否是master
     node.master: true
    # 是否存储数据
     node.data: true
    # ES的监听地址,这样别的机器也可以访问,有的教程说此项设置成0.0.0.0代表任意ip都可以访问本es
     network.host: 0.0.0.0
    # 默认的就好,es启动后通过web访问es,只需输入ip:9200,出现一个json即代表成功
     http.port: 9200
    # 本机最大允许运行节点个数,默认即可
     node.max_local_storage_nodes: 3
    # 增加新的参数,这样head插件可以访问es,解决跨域访问问题,一般用不到
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    
  • 启动

    #切换用户 并启动es
    $ su  esuser
    $ ./elasticsearch/bin/elasticsearch
    
  • 测试

    $ curl http://localhost:9200/?pretty
    
2、集群部署

​ 注意系统首先要做时间同步。

​ 集群部署方面,ES有两种组合集群的方式,一种是组播自动发现集群,一种是单播组集群,另外一种是组播自动发现集群

  • 组播自动发现集群

    部署时只要设置相同的cluster.name,相同的transport.tcp.port,不同的node.name即可自动发现集群,无需声明节点情况

  • 单播组集群

    单播组集群的方案中,除了要设置相同的cluster.name,相同的transport.tcp.port,不同的node.name外,还要设置一下参数

    • 安装部署ES

      下载->解压->创建用户->修改权限->Java环境准备(高版本自带)->修改配置文件->启动

    • Elasticsearch集群中的三种角色

      • master node:master几点主要用于元数据(metadata)的处理,比如索引的新增、删除、分片分配等;
      • data node:data 节点上保存了数据分片。它负责数据相关操作,比如分片的 CRUD,以及搜索和整合操作。这些操作都比较消耗 CPU、内存和 I/O 资源;
      • client node:client 节点起到路由请求的作用,实际上可以看做负载均衡器。
      # 配置文件中给出了三种配置高性能集群拓扑结构的模式,如下: 
      # 1. 如果你想让节点从不选举为主节点,只用来存储数据,可作为负载器 
        node.master: false 
        node.data: true 
      # 2. 如果想让节点成为主节点,且不存储任何数据,并保有空闲资源,可作为协调器
        node.master: true
        node.data: false
      # 3. 如果想让节点既不称为主节点,又不成为数据节点,那么可将他作为搜索器,从节点中获取数据,生成搜索结果等 
        node.master: false 
        node.data: false
      
    • 配置项说明

      • cluster_name 集群名称,默认为elasticsearch,这里我们设置为es5.2.1Cluster
      • node.name配置节点名,用来区分节点
      • network.host 是配置可以访问本节点的路由地址
      • http.port 路由地址端口
      • transport.tcp.port TCP协议转发地址端口
      • node.master 是否作为集群的主结点 ,值为true或true
      • node.data 是否存储数据,值为true或true
      • discovery.zen.ping.unicast.hosts 用来配置所有用来组建集群的机器的IP地址,由于5.2.1新版本是不支持多播的,因此这个值需要提前设定好,当集群需要扩展的时候,该值都要做改变,增加新机器的IP地址,如果是在一个ip上,要把TCP协议转发端口写上
      • discovery.zen.minimum_master_nodes 用来配置主节点数量的最少值,如果主节点数量低于该值,闭包范围内的集群将会停止服务,之所以加粗体,是因为暂时尚未认证,下面配置为1方便集群更容易形成,即使只有一个主节点,也可以构建集群
      • gateway.* 网关的相关配置
      • script.* indices.* 根据需求添加的配置(可选)
    • 详细配置文件

      例:两台机器,一台上有3个es节点

      cluster.name: elap 
      path.data: /data/01,/data/02,/data/03,/data/04,/data/05,/data/06    ##这些目录elasticsearch用户有访问修改权限
      path.repo: /data/24/repo01    ##这些目录elasticsearch用户有访问修改权限
      path.logs: /data/24/logs01    ##这些目录elasticsearch用户有访问修改权限
      script.inline: false
      script.search: false
      script.engine.painless.inline: true
      script.engine.painless.stored.search: true
      script.engine.painless.stored.aggs: true
      script.painless.regex.enabled: true
      network.host: 0.0.0.0
      bootstrap.system_call_filter: false
      http.port: 9200
      transport.tcp.port: 9300
      http.cors.enabled: true
      http.cors.allow-origin: "*"
      node.master: false     ##es01/es02是node.master:false,es03是node.master:true
      node.data: true          ##es01/es02是node.data:true,es03是node.data:false
      discovery.zen.ping.unicast.hosts: ["10.19.24.31:9300","10.19.24.31:9500","10.19.24.31:9700","10.19.24.32:9300","10.19.24.32:9500","10.19.24.32:9700"]    ##elasticsearch中所有节点的数据交换端口
      discovery.zen.minimum_master_nodes: 1    
      node.max_local_storage_nodes: 3    ##每台机器上的es节点数
      
    • 修改jvm限制内存使用

       -Xms30g    ##es最大内存使用限制30g
       -Xmx30g
       ......
       -XX:+HeapDumpOnOutOfMemoryError    ##添加本行内容
      
  • 集群状态查看

    • 集群健康状态

      $ curl http://localhost:9200/_cluster/health?pretty
      {
        "cluster_name" : "****",
        "status" : "yellow",
        "timed_out" : false,
        "number_of_nodes" : 1,
        "number_of_data_nodes" : 1,
        "active_primary_shards" : 130,
        "active_shards" : 130,
        "relocating_shards" : 0,
        "initializing_shards" : 0,
        "unassigned_shards" : 46,
        "delayed_unassigned_shards" : 0,
        "number_of_pending_tasks" : 0,
        "number_of_in_flight_fetch" : 0,
        "task_max_waiting_in_queue_millis" : 0,
        "active_shards_percent_as_number" : 73.86363636363636
      }
      
      ## 指标说明
      status:集群状态,分为green、yellow和red。 
      number_of_nodes/number_of_data_nodes:集群的节点数和数据节点数。 
      active_primary_shards:集群中所有活跃的主分片数。 
      active_shards:集群中所有活跃的分片数。 
      relocating_shards:当前节点迁往其他节点的分片数量,通常为0,当有节点加入或者退出时该值会增加。 
      initializing_shards:正在初始化的分片。 
      unassigned_shards:未分配的分片数,通常为0,当有某个节点的副本分片丢失该值就会增加。 
      number_of_pending_tasks:是指主节点创建索引并分配shards等任务,如果该指标数值一直未减小代表集群存在不稳定因素 
      active_shards_percent_as_number:集群分片健康度,活跃分片数占总分片数比例。 
      number_of_pending_tasks:pending task只能由主节点来进行处理,这些任务包括创建索引并将shards分配给节点。
      
    • 集群状态信息获取

      $ curl http://localhost:9200/_cluster/stats?pretty
      ## 输出内容过多,不进行展示
      
      ## 关键指标说明
      indices.count:索引总数。 
      indices.shards.total:分片总数。 
      indices.shards.primaries:主分片数量。 
      docs.count:文档总数。 
      store.size_in_bytes:数据总存储容量。 
      segments.count:段总数。 
      nodes.count.total:总节点数。 
      nodes.count.data:数据节点数。 
      nodes. process. cpu.percent:节点CPU使用率。 
      fs.total_in_bytes:文件系统使用总容量。 
      fs.free_in_bytes:文件系统剩余总容量。
      
  • 节点状态查看

    $ curl http://localhost:9200/_node/stats?pretty
    ## 输出内容过多,不进行展示
    

    关键指标说明:

    name:节点名。
    roles:节点角色。
    indices.docs.count:索引文档数。
    segments.count:段总数。
    jvm.heap_used_percent:内存使用百分比。
    thread_pool.{bulk, index, get, search}.{active, queue, rejected}:线程池的一些信息,包括bulk、index、get和search线程池,主要指标有active(激活)线程数,线程queue(队列)数和rejected(拒绝)线程数量。

    以下一些指标是一个累加值,当节点重启之后会清零。
    indices.indexing.index_total:索引文档数。
    indices.indexing.index_time_in_millis:索引总耗时。
    indices.get.total:get请求数。
    indices.get.time_in_millis:get请求总耗时。
    indices.search.query_total:search总请求数。
    indices.search.query_time_in_millis:search请求总耗时。indices.search.fetch_total:fetch操作总数量。
    indices.search.fetch_time_in_millis:fetch请求总耗时。
    jvm.gc.collectors.young.collection_count:年轻代垃圾回收次数。
    jvm.gc.collectors.young.collection_time_in_millis:年轻代垃圾回收总耗时。
    jvm.gc.collectors.old.collection_count:老年代垃圾回收次数。
    jvm.gc.collectors.old.collection_time_in_millis:老年代垃圾回收总耗时。

3、动态加入节点
  • 在新节点安装部署elasticsearch

  • 修改新节点的配置文件

    # 配置同上
      discovery.zen.ping.unicast.hosts: ["集群中所有节点IP"] 
    
  • 修改旧节点的配置文件

    # 修改
      discovery.zen.ping.unicast.hosts: ["加入新增节点IP"] 
    
  • 重启ES集群

四、ES使用

1、交互使用方式

​ Elasticsearch提供了多种交互使用方式,包括Java API和RESTful API 。所有其他语言可以使用RESTful API 通过端口 9200 和 Elasticsearch 进行通信,你可以用你最喜爱的 web 客户端访问 Elasticsearch 。甚至,你还可以使用 curl 命令来和 Elasticsearch 交互。

​ 一个Elasticsearch请求和任何 HTTP 请求一样,都由若干相同的部件组成:

curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'

返回的数据格式为JSON,因为Elasticsearch中的文档以JSON格式储存。其中,被 **< >**标记的部件:

部件说明
VERB适当的 HTTP 方法 或 谓词 : GET、 POST、 PUT、 HEAD 或者 DELETE
PROTOCOLhttp或者 https(如果你在 Elasticsearch 前面有一个 https代理)
HOSTElasticsearch 集群中任意节点的主机名,或者用 localhost 代表本地机器上的节点
PORT运行 Elasticsearch HTTP 服务的端口号,默认是 9200
PATHAPI 的终端路径(例如 _count 将返回集群中文档数量)。Path 可能包含多个组件,例如: _cluster/stats 和 _nodes/stats/jvm
QUERY_STRING任意可选的查询字符串参数 (例如 ?pretty 将格式化地输出 JSON 返回值,使其更容易阅读)
BODY一个 JSON 格式的请求体 (如果请求需要的话)

对于HTTP方法,它们的具体作用为:

HTTP方法说明
GET获取请求对象的当前状态
POST改变对象的当前状态
PUT创建一个对象
DELETE销毁对象
HEAD请求获取对象的基础信息

例:计算集群中文档的数量:

curl -XGET 'http://localhost:9200/_count?pretty' -d '
{
    "query": {
        "match_all": {}
    }
}'

Elasticsearch 返回一个 HTTP 状态码(例如:200 OK)和(除HEAD请求)一个 JSON 格式的返回值。前面的 curl请求将返回一个像下面一样的 JSON 体

{
    "count" : 0,
    "_shards" : {
        "total" : 5,
        "successful" : 5,
        "failed" : 0
    }
}

在返回结果中没有看到 HTTP 头信息是因为我们没有要求curl显示它们。想要看到头信息,需要结合 -i 参数来使用 curl命令:

2、集群查看命令
  • _cat

    $ curl localhost:9200/_cat
    =^.^=
    /_cat/allocation
    /_cat/shards
    /_cat/shards/{index}
    /_cat/master
    /_cat/nodes
    /_cat/indices
    /_cat/indices/{index}
    /_cat/segments
    /_cat/segments/{index}
    /_cat/count
    /_cat/count/{index}
    /_cat/recovery
    /_cat/recovery/{index}
    /_cat/health
    /_cat/pending_tasks
    /_cat/aliases
    /_cat/aliases/{alias}
    /_cat/thread_pool
    /_cat/plugins
    /_cat/fielddata
    /_cat/fielddata/{fields}
    /_cat/nodeattrs
    /_cat/repositories
    /_cat/snapshots/{repository}
    
  • verbose

    每个命令都支持使用?v参数,来显示详细的信息

    $ curl localhost:9200/_cat/master?v
    id                     host            ip              node
    FGOaj0xxSj201cHSQEtoLA 192.168.113.138 192.168.113.138 FGOaj0x
    
  • help

    每个命令都支持使用help参数,来输出可以显示的列

    $ curl localhost:9200/_cat/master?help
    id   |   | node id    
    host | h | host name  
    ip   |   | ip address 
    node | n | node name
    
  • headers

    通过h参数,可以指定输出的字段

    $ curl localhost:9200/_cat/master?v
    id                     host            ip              node
    FGOaj0xxSj201cHSQEtoLA 192.168.113.138 192.168.113.138 FGOaj0x
    $ curl localhost:9200/_cat/master?h=ip,node
    192.168.113.138 FGOaj0x
    
  • Sort

    指定输出的列进行排序,默认按照升序排序

    $ curl localhost:9200/_cat/master?v&s=ip,id:desc
    
  • Format

    指定响应返回的数据格式:text(默认),json,yaml,smile,cbor(通过设置 Accept的HTTP头部的多媒体格式的优先级更高 )

    $ curl localhost:9200/_cat/master?format=json
    

五、ES集群优化

原文:https://blog.csdn.net/gamer_gyt/article/details/62217053?utm_source=copy

1、集群节点角色分配

之前介绍了es集群中得三个角色 master ,data,client(详情见第三章)

  • 关闭data节点的http功能

    针对ES集群中的所有数据节点,不用开启http服务。将其中的配置 参数这样设置:http.enabled: false,同时也不要安装head, bigdesk, marvel等监控 插件,这样保证data节点服务器只需处理创建/更新/删除/查询索引数据等操作。

    http功能可以在非数据节点服务器上开启,上述相关的监控插件也安装到这些服务器上,用于监控ElasticSearch集群状态等数据信息。这样做一来出于数据安全考虑,二来出于服务性能考虑。

  • 一台服务器上最好只部署一个node

    一台物理服务器上可以启动多个Node服务器节点(通过设置不同的启动port),但一台服务器上的CPU,内存,硬盘等资源毕竟有限,从服务器性能考虑,不建议一台服务器上启动多个node节点。

    在大规模局点,比如100个点,可以专门配备3个Master,可使用3台具有内存的刀片即可,即参数配置为node.master: true,node.data: false;可以按比例配备数据汇聚节点,比如10个,即参数配置为node.master: false ,node.data: false;小规模节点,可以不用如此设置,当然如果依然有性能问题,也是一个优化的措施

2、集群的内存设置

Elasticsearch 默认采用的是Lucene,至于为什么es采用这个,原因可能是因为Lucene是一个成熟的、高性能的、可扩展的、轻量级的,而且功能强大的搜索引擎包。Lucene的核心jar包只有一个文件,而且不依赖任何第三方jar包。更重要的是,它提供的索引数据和检索数据的功能开箱即用。当然,Lucene也提供了多语言支持,具有拼写检查、高亮等功能。当然es使用Lucene作为分词搜索包,势必会造成很大程度上的内存消耗。

  • 预留一半内存给Lucene使用

    一个常见的问题是配置堆太大。你有一个64 GB的机器,觉得JVM内存越大越好,想给Elasticsearch所有64 GB的内存。

    当然,内存对于Elasticsearch来说绝对是重要的,用于更多的内存数据提供更快的操作。而且还有一个内存消耗大户-Lucene

    Lucene的设计目的是把底层OS里的数据缓存到内存中。Lucene的段是分别存储到单个文件中的,这些文件都是不会变化的,所以很利于缓存,同时操作系统也会把这些段文件缓存起来,以便更快的访问。

    Lucene的性能取决于和OS的交互,如果你把所有的内存都分配给Elasticsearch,不留一点给Lucene,那你的全文检索性能会很差的。

    最后标准的建议是把50%的内存给elasticsearch,剩下的50%也不会没有用处的,Lucene会很快吞噬剩下的这部分内存。

  • 32GB限制

    在java中,所有的对象都分配在堆上,然后有一个指针引用它。指向这些对象的指针大小通常是CPU的字长的大小,不是32bit就是64bit,这取决于你的处理器,指针指向了你的值的精确位置。

    对于32位系统,你的内存最大可使用4G。对于64系统可以使用更大的内存。但是64位的指针意味着更大的浪费,因为你的指针本身大了。浪费内存不算,更糟糕的是,更大的指针在主内存和缓存器(例如LLC, L1等)之间移动数据的时候,会占用更多的带宽。

    java 使用一个叫内存指针压缩的技术来解决这个问题。它的指针不再表示对象在内存中的精确位置,而是表示偏移量。这意味着32位的指针可以引用40亿个对象,而不是40亿个字节。最终,也就是说堆内存长到32G的物理内存,也可以用32bit的指针表示。

    一旦你越过那个神奇的30-32G的边界,指针就会切回普通对象的指针,每个对象的指针都变长了,就会使用更多的CPU内存带宽,也就是说你实际上失去了更多的内存。事实上当内存到达40-50GB的时候,有效内存才相当于使用内存对象指针压缩技术时候的32G内存。

    这段描述的意思就是说:即便你有足够的内存,也尽量不要超过32G,因为它浪费了内存,降低了CPU的性能,还要让GC应对大内存。

  • 机器内存大于64GB

    可以考虑一台机器上创建两个或者更多ES节点,而不要部署一个使用32+GB内存的节点。仍然要 坚持50%原则,假设 你有个机器有128G内存,你可以创建两个node,使用32G内存。也就是说64G内存给ES的堆内存,剩下的64G给Lucene。

    但第二种需要配置下面的配置项,防止同一个shard的主副本存在同一个物理机上(因为如果存在一个机器上,副本的高可用性就没有了)

    cluster.routing.allocation.same_shard.host:true
    
  • ES集群的heap参数优化

    所谓的heap即数据缓存的内存大小,ES集群中消耗内存的有以下几个:

    • segment Memory

      Lucene 把每次生成的倒排索引,叫做一个段(segment)。然后另外使用一个 commit 文件,记录索引内所有的 segment。而生成 segment 的数据来源,则是内存中的 buffer。由于词典的size会很大,全部装载到heap里不现实,因此Lucene为词典做了一层前缀索引(Term Index),这个索引在Lucene4.0以后采用的数据结构是FST (Finite State Transducer)。这种数据结构占用空间很小,Lucene打开索引的时候将其全量装载到内存中,加快磁盘上词典查询速度的同时减少随机磁盘访问次数。所以ES的data node存储数据并非只是耗费磁盘空间的,为了加速数据的访问,每个segment都有会一些索引数据驻留在heap里。因此segment越多,瓜分掉的heap也越多,并且这部分heap是无法被GC掉的! 理解这点对于监控和管理集群容量很重要,当一个node的segment memory占用过多的时候,就需要考虑删除、归档数据,或者扩容了。

    • filter cache

      Filter cache是用来缓存使用过的filter的结果集的,需要注意的是这个缓存也是常驻heap,无法GC的。默认的10% heap size设置工作得够好了,如果实际使用中heap没什么压力的情况下,才考虑加大这个设置。

    • field data cache

      对搜索结果做排序或者聚合操作,需要将倒排索引里的数据进行解析,然后进行一次倒排。在有大量排序、数据聚合的应用场景,可以说field data cache是性能和稳定性的杀手。这个过程非常耗费时间,因此ES2.0以前的版本主要依赖这个cache缓存已经计算过的数据,提升性能。但是由于heap空间有限,当遇到用户对海量数据做计算的时候,就很容易导致heap吃紧,集群频繁GC,根本无法完成计算过程。ES2.0以后,正式默认启用Doc Values特性(1.x需要手动更改mapping开启),将field data在indexing time构建在磁盘上,经过一系列优化,可以达到比之前采用field data cache机制更好的性能。因此需要限制对field data cache的使用,最好是完全不用,可以极大释放heap压力。这里需要注意的是,排序、聚合字段必须为not analyzed。设想如果有一个字段是analyzed过的,排序的实际对象其实是词典,在数据量很大情况下这种情况非常致命。

    • Bulk Queue

      Bulk Queue是做什么用的?当所有的bulk thread都在忙,无法响应新的bulk request的时候,将request在内存里排列起来,然后慢慢清掉。一般来说,Bulk queue不会消耗很多的heap,但是见过一些用户为了提高bulk的速度,客户端设置了很大的并发量,并且将bulk Queue设置到不可思议的大,比如好几千。这在应对短暂的请求爆发的时候有用,但是如果集群本身索引速度一直跟不上,设置的好几千的queue都满了会是什么状况呢? 取决于一个bulk的数据量大小,乘上queue的大小,heap很有可能就不够用,内存溢出了。一般来说官方默认的thread pool设置已经能很好的工作了,建议不要随意去“调优”相关的设置,很多时候都是适得其反的效果。

    • Indexing Buffer

      Indexing Buffer是用来缓存新数据,当其满了或者refresh/flush interval到了,就会以segment file的形式写入到磁盘。这个参数的默认值是10% heap size。根据经验,这个默认值也能够很好的工作,应对很大的索引吞吐量。但有些用户认为这个buffer越大吞吐量越高,因此见过有用户将其设置为40%的。到了极端的情况,写入速度很高的时候,40%都被占用,导致OOM。

    • Cluster State Buffer

      ES被设计成每个Node都可以响应用户的api请求,因此每个Node的内存里都包含有一份集群状态的拷贝。这个Cluster state包含诸如集群有多少个Node,多少个index,每个index的mapping是什么?有多少shard,每个shard的分配情况等等(ES有各类stats api获取这类数据)。在一个规模很大的集群,这个状态信息可能会非常大的,耗用的内存空间就不可忽视了。并且在ES2.0之前的版本,state的更新是由Master Node做完以后全量散播到其他结点的。频繁的状态更新都有可能给heap带来压力。在超大规模集群的情况下,可以考虑分集群并通过tribe node连接做到对用户api的透明,这样可以保证每个集群里的state信息不会膨胀得过大。

    • 超大搜索聚合结果集的fetch

      ES是分布式搜索引擎,搜索和聚合计算除了在各个data node并行计算以外,还需要将结果返回给汇总节点进行汇总和排序后再返回。无论是搜索,还是聚合,如果返回结果的size设置过大,都会给heap造成很大的压力,特别是数据汇聚节点。

  • 优化建议

    一般分配主机1/4-1/2的内存

3、集群的硬盘和CPU设置
  • 硬盘选型

    硬盘对集群非常重要,特别是建索引多的情况。磁盘是一个服务器最慢的系统,对于写比较重的集群,磁盘很容易成为集群的瓶颈。如果可以承担机器SSD盘,最好使用SSD盘。如果使用SSD,最好调整I/O调度算法。RAID0是加快速度的不错方法。

  • 自动调整存储带宽

    在2.0.0之前,elasticsearch会限制合并速度(merges),默认为20MB/sec。但是这个速率经常是显得太小,导致合并速度落后于索引速度,进而限制了索引速度。

    现在Elasticsearch2.0.0之后,使用了自动调整合并IO速度方式:如果合并落于索引速度,合并IO速度会逐渐增大,并且随着合并的持续进行会减小。在索引吞吐量小的时候,即使突然来了一个大的合并任务,这种情况也不会吞噬整个节点可用的IO,极小化的降低对正在进行的查询和索引的影响。

    但是对索引请求大的情况下,允许的合并速度会自动调整到跟上索引的速度。有了2.0.0这个特性,意味着我们不需要管任何的限制值了,只要用默认的就好了。

  • 多个path.data路径

    如果磁盘空间和IO性能是Elasticsearch的瓶颈的话,使用多个IO设备(通过设置多个path.data路径)存储shards,能够增加总的存储空间和提升IO性能。

    在Elasticsearch2.0之前的版本,也是配置多个path.data路径,但是其相当于RAID 0,每个shards的数据会分布在所有的磁盘上。当一个节点上有一块盘坏了的情况下,该节点上所有的shards都会损坏了。需要恢复该节点上的所有shards。

    在2.0.0版本,把这个实现改成了:每个shards所有的数据只会在一块磁盘上面。这样即使一个节点的一块磁盘损坏了,也只是损失了该磁盘上的shards,其它磁盘上的shards安然无事。只需要恢复该块盘上的shards即可。

    升级到2.0.0版本时,旧版本一个shard分布到所有磁盘上的数据,会拷贝到一块盘上。

    对应这个改变,在设计shards时,如果一个节点有10块磁盘,共3个节点,则shards至少30个,才能分布在30块盘上(即最大限度使用磁盘空间)。

4、集群的分片和副本配置
  • 分片(Shard)

    一个索引会分成多个分片存储,分片数量在索引建立后不可更改
    分片数是与检索速度非常相关的的指标,如果分片数过少或过多都会导致检索比较慢。分片数过多会导致检索时打开比较多的文件别外也会导致多台服务器之间通讯。而分片数过少会导致单个分片索引过大,所以检索速度慢。基于索引分片数=数据总量/单分片数的计算公式,在确定分片数之前需要进行单服务单索引单分片的测试。

  • 副本(replicas)

    每个索引的数据备份数量。
    ElasticSearch在创建索引数据时,最好指定相关的shards数量和replicas, 否则会使用服务器中的默认配置参数shards=5,replicas=1。

这两个属性的设置直接影响集群中索引和搜索操作的执行。假设有足够的机器来持有碎片和副本,那么可以按如下规则设置这两个值:

拥有更多的分片可以提升索引执行能力,并允许通过机器分发一个大型的索引;

拥有更多的副本能够提升搜索执行能力以及集群能力。

对于一个索引来说,number_of_shards只能设置一次,而number_of_replicas可以使用索引更新设置API在任何时候被增加或者减少。这两个配置项如下:

index.number_of_shards: 5       #设置默认索引分片个数,默认为5片
index.number_of_replicas: 1     #设置默认索引副本个数,默认为1个副本

ES官方文档建议:一个Node中一个索引最好不要多于三个shards。配置total_shards_per_node参数,限制每个index每个节点最多分配多少个发片。

5、集群优化总结
  • java jdk版本尽量高一点,否则容易出现bug
  • es集群结点规划好,master,client,data node 分开,关闭data node 的http功能
  • 合理利用内存
  • 根据机器数,磁盘数,索引大小等硬件环境,根据测试结果,设置最优的分片数和备份数,单个分片最好不超过10GB,定期删除不用的索引,做好冷数据的迁移
  • 保守配置内存限制参数,尽量使用doc value存储以减少内存消耗,查询时限制size、from参数
  • 结合实际场景,做好集群监控

六、ES集群故障记录及维护

1、集群有未分配的分片问题
  • 查看ES集群的健康状态

    # curl http://localhost:9200/_cluster/health?pretty
    
    ## 输出省略
    ## 集群状态是red
    "status" : "red"
    ## 未分配的分片数不为0
    "unassigned_shards" : 3    
    
  • 查看未分配的原因

    # curl http://localhost:9200/_cluster/allocation/explain?pretty
    ## 返回未分配索引每个分片的详情和未分配的原因
    
    # curl http://localhost:9200/_cat/indices?v&health=red
    ## 查看健康状态为red的索引的信息
    
    # curl -XGET http://localhost:9200/_cat/shards?h=index,shard,prirep,state,unassigned.reason| grep UNASSIGNED
    ## 分析出未分配的分片信息及未分配的原因
    ## 未分配的原因类型
    ALLOCATION_FAILED:由于分片分配失败而未分配。
    CLUSTER_RECOVERED:由于集群恢复而未分配。
    DANGLING_INDEX_IMPORTED:由于导入了悬空索引导致未分配。
    EXISTING_INDEX_RESTORED:由于恢复为已关闭的索引导致未分配。
    INDEX_CREATED:由于API创建索引而未分配。
    INDEX_REOPENED:由于打开已关闭索引而未分配。
    NEW_INDEX_RESTORED:由于恢复到新索引而未分配。
    NODE_LEFT:由于托管的节点离开集群而未分配。
    REALLOCATED_REPLICA:确定了更好的副本位置,并导致现有副本分配被取消。
    REINITIALIZED:当分片从开始移动回初始化,导致未分配。
    REPLICA_ADDED:由于显式添加副本而未分配。
    REROUTE_CANCELLED:由于显式取消重新路由命令而未分配。
    

    发现有两个主分片是因为分片分配失败而未分配

  • 尝试重新分配失败的分片

    # curl -XPOST http://localhost:9200/_cluster/reroute?retry_failed=true
    

    尝试后未分配的两个主分片还是没有分配

  • 尝试重新启停集群

  • 尝试重新关开索引

    # curl -XPOST http://localhost:9200/index/_close
    # curl -XPOST http://localhost:9200/index/_open
    

    到这时分片还是没有分配成功

  • 将副本分片提升为主分片

    确定了主分片已经损坏,可以尝试将副本分片提升为主分片,但是会丢部分数据

    查找此分片的副本位于哪个节点用以指定

    # curl 'http://localhost:9200/_shard_stores?pretty'
    # curl 'http://localhost:9200/indexname/_shard_stores?pretty'
    

    手动调用集群的 reroute接口,在接受部分数据丢失的情况下,将节点上的原有副本,强制提升为索引的主分片

    # curl -XPOST 'http://localhost:9200/_cluster/reroute?master_timeout=5m&pretty' -d ‘{
      "commands": [
        {
            "allocate_stale_primary": {
            "index": "indexname",//索引名
            "shard": 3,//操作的分片id
            "node": "node1",//此分片副本位于的节点
            "accept_data_loss": true//提示数据可能会丢失
          }
        }
      ]
    }
  • 如果未分配的主分片没有副本,或者副本也损坏

    可将此分片置为空以保留索引其他分片数据

    # curl -XPOST 'http://localhost:9200/_cluster/reroute?master_timeout=5m&pretty' -d ‘{
      "commands": [
        {
            "allocate_empty_primary": {
            "index": "indexname",//索引名
            "shard": 3,//操作的分片id
            "node": "node1",//此分片副本位于的节点
            "accept_data_loss": true//提示数据可能会丢失
          }
        }
      ]
    }

    注意:在通过CURL和ES进行交互时报错"error" : “Content-Type header [application/x-www-form-urlencoded] is not supported”,这是因为application/x-www-form-urlencoded不支持Json发送,需要改成application/Json,所以添加参数-H 'content-Type:application/json’即可

  • 参考

    https://blog.csdn.net/kezhen/article/details/79379512

    https://blog.csdn.net/qq_16164711/article/details/119822139

    https://www.cnblogs.com/TopGear/p/14549092.html

2、ES升级
;