ElasticSearch架构介绍及原理解析

目录

简介

架构原理

基本概念与MySQL的对比

分片机制

document路由原理

集群发现机制

shard&replica规则

避免脑裂

负载均衡

相关配置

容错过程与选举机制

扩容机制

容错机制

Lucene

结构原理

Lucene索引实现

DocValues

关于ES索引与检索分片

运行原理解析

倒排索引说明

检索倒排索引

分词器Analyzer

建立索引和类型

分片内文档写入流程场景

多个分片的文档写入场景

确定文档存储位置

同步副本

根据_id查询文档场景

根据字段值检索数据场景

调优

分片的数量

JVM设置

小技巧

开发说明


简介

Elasticsearch是一个分布式、可扩展、实时的搜索与数据分析引擎。它不仅仅只是全文搜索,还支持结构化搜索、数据分析、复杂的语言处理、地理位置和对象间关联关系等。

ES的底层依赖Lucene。Lucene当下最先进、高性能、全功能的搜索引擎库。但是Lucene仅仅只是一个库,为了充分发挥其功能,需要使用Java并将Lucene直接集成到应用程序中。鉴于Lucene如此强大却难以上手的特点,诞生了ES。ES也是使用Java编写的,它的内部使用Lucene做索引与搜索,它的目的是隐藏Lucene的复杂性,取而代之的提供一套简单一致的RESTful API。

总体来说,ES具有如下特点:

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

架构原理

ElasticSearch架构介绍及原理解析_第1张图片

基本概念与MySQL的对比

ElasticSearch MySQL
Index Database
Type (6.0以上版本弃用) Table
Document Row
Field Column
Mapping Schema
Everything is indexed Index
Query DSL SQL
GET HTTP SELECT
PUT HTTP UPDATE

ES中存储数据的基本单位是索引,比如说ES中存储了一些订单系统的销售数据,就应该在ES中创建一个索引order_index,所有的销售数据都会写到这个索引里面去,一个索引就像一个数据库,而type就相当于库里的表,一个index里面可以有多个type,而mapping就相当于表的结构定义,定义了字段的类型等,往index的一个type里添加一行数据,这行数据就叫做一个document,每个document有多个filed,每一个filed就代表这个document的一个字段的值。

运行流程:

ES客户端将一份数据写入primary shard,然后将数据同步到replica shard中去。ES客户端取数据的时候就会在replica或primary的shard中去读。ES集群有多个节点,会自动选举一个节点为master节点,这个master节点干一些管理类的操作,比如维护元数据,负责切换primary shard和replica shard的身份,要是master节点宕机了,那么就会重新选举下一个节点为master为节点。如果primary shard所在的节点宕机了,那么就会由master节点将那个宕机的节点上的primary shard的身份转移到replica shard上,如果修复了宕机的那台机器,重启之后,master节点就会将缺失的replica shard分配过去,同步后续的修改工作,让集群恢复正常。

分片机制

在建立索引时,会自动将数据拆分到多个分片(shard)中,默认数量是5,这个就是索引数据分片机制。

document路由原理

document要存储到Elasticsearch中,还要满足后续搜索的需求,路由到分片位置的算法肯定不能是随机的,要不然搜索就没法找了,路由的过程有一个公式:

shard = hash(routing) % number_of_primary_shards

routing值默认是document的ID值,也可以自行指定。先对routing信息求hash值,然后将hash结果对primary_shard的数量求模,比如说primary_shard是5,那么结果肯定落在[0,4]区间内,这个结果值就是该document的分片位置,如示意图所示:

ElasticSearch架构介绍及原理解析_第2张图片

这个求模公式间接的解释了为什么了索引创建时指定了primary shard的值,后续就不让改了,模数改了,之前路由的document再执行该公式时,值就可能跟改之前得到的值不一致,这样document就找不到了,如示意图所示:

ElasticSearch架构介绍及原理解析_第3张图片

集群发现机制

在同一个网络环境下,只要启动一个Elasticsearch实例,并且cluster.name配置得一样,这个Elasticsearch实例就会自动加入到集群当中,这个是如何实现的?

这个依赖于Elasticsearch的自动发现机制Zen,在elasticsearch.yml配置文件中,有一行

discovery.zen.ping.unicast.hosts: ["192.168.17.137"]

表示单播发现方式,当该Elasticsearch实例启动时,会向192.168.17.137主机发送请求,并得到整个集群里所有节点的状态,然后去联系master节点,并加入集群。

摘抄了获取配置信息,注册discovery请求的部分源码如下:

org.elasticsearch.discovery.zen.ZenDiscovery启动时的构造器,会调用org.elasticsearch.discovery.zen.UnicastZenPing的构造器,其中UnicastZenPing的构造方式内会加载discovery.zen.ping.unicast.hosts配置项,并发送"internal:discovery/zen/unicast"请求(代码有删节):

public UnicastZenPing(Settings settings, ThreadPool threadPool, TransportService transportService,
                          UnicastHostsProvider unicastHostsProvider, PingContextProvider contextProvider) {
        super(settings);
        final int concurrentConnects = DISCOVERY_ZEN_PING_UNICAST_CONCURRENT_CONNECTS_SETTING.get(settings);
        if (DISCOVERY_ZEN_PING_UNICAST_HOSTS_SETTING.exists(settings)) {
            configuredHosts = DISCOVERY_ZEN_PING_UNICAST_HOSTS_SETTING.get(settings);
            // we only limit to 1 addresses, makes no sense to ping 100 ports
            limitPortCounts = LIMIT_FOREIGN_PORTS_COUNT;
        } else {
            // if unicast hosts are not specified, fill with simple defaults on the local machine
            configuredHosts = transportService.getLocalAddresses();
            limitPortCounts = LIMIT_LOCAL_PORTS_COUNT;
        }
        resolveTimeout = DISCOVERY_ZEN_PING_UNICAST_HOSTS_RESOLVE_TIMEOUT.get(settings);
        transportService.registerRequestHandler(ACTION_NAME, ThreadPool.Names.SAME, UnicastPingRequest::new,
            new UnicastPingRequestHandler());
    }

shard&replica规则

一个index的数据,是拆分存储在多个shard当中,我们可以在Elasticsearch的数据目录里查看一下索引的存储结构(Elasticsearch服务器上导出的树状目录结构):

.
└── nodes
    └── 0
        ├── indices
        │   ├── 48G_CgE7TiWomlYsyQW1NQ #索引location的UUID
        │   │   ├── 0 #primary shard,从0-4共5个
        │   │   │   ├── index
        │   │   │   │   ├── segments_3
        │   │   │   │   └── write.lock
        │   │   │   ├── _state
        │   │   │   │   └── state-2.st
        │   │   │   └── translog
        │   │   │       ├── translog-2.ckp
        │   │   │       ├── translog-2.tlog
        │   │   │       ├── translog-3.ckp
        │   │   │       ├── translog-3.tlog
        │   │   │       ├── translog-4.tlog
        │   │   │       └── translog.ckp
        │   │   ├── 1
        │   │   │   ├── index
        │   │   │   │   ├── segments_3
        │   │   │   │   └── write.lock
        │   │   │   ├── _state
        │   │   │   │   └── state-2.st
        │   │   │   └── translog
        │   │   │       ├── translog-2.ckp
        │   │   │       ├── translog-2.tlog
        │   │   │       ├── translog-3.ckp
        │   │   │       ├── translog-3.tlog
        │   │   │       ├── translog-4.tlog
        │   │   │       └── translog.ckp
        │   │   ├── 2
        │   │   │   ├── index
        │   │   │   │   ├── _1.cfe
        │   │   │   │   ├── _1.cfs
        │   │   │   │   ├── _1.si
        │   │   │   │   ├── segments_7
        │   │   │   │   └── write.lock
        │   │   │   ├── _state
        │   │   │   │   └── state-2.st
        │   │   │   └── translog
        │   │   │       ├── translog-4.ckp
        │   │   │       ├── translog-4.tlog
        │   │   │       ├── translog-5.ckp
        │   │   │       ├── translog-5.tlog
        │   │   │       ├── translog-6.tlog
        │   │   │       └── translog.ckp
        │   │   ├── 3
        │   │   │   ├── index
        │   │   │   │   ├── _1.cfe
        │   │   │   │   ├── _1.cfs
        │   │   │   │   ├── _1.si
        │   │   │   │   ├── segments_7
        │   │   │   │   └── write.lock
        │   │   │   ├── _state
        │   │   │   │   └── state-2.st
        │   │   │   └── translog
        │   │   │       ├── translog-4.ckp
        │   │   │       ├── translog-4.tlog
        │   │   │       ├── translog-5.ckp
        │   │   │       ├── translog-5.tlog
        │   │   │       ├── translog-6.tlog
        │   │   │       └── translog.ckp
        │   │   ├── 4
        │   │   │   ├── index
        │   │   │   │   ├── _0.cfe
        │   │   │   │   ├── _0.cfs
        │   │   │   │   ├── _0.si
        │   │   │   │   ├── segments_5
        │   │   │   │   └── write.lock
        │   │   │   ├── _state
        │   │   │   │   └── state-2.st
        │   │   │   └── translog
        │   │   │       ├── translog-3.ckp
        │   │   │       ├── translog-3.tlog
        │   │   │       ├── translog-4.ckp
        │   │   │       ├── translog-4.tlog
        │   │   │       ├── translog-5.tlog
        │   │   │       └── translog.ckp
        │   │   └── _state
        │   │       └── state-16.st
        ├── node.lock
        └── _state
            ├── global-88.st
            └── node-22.st

如上目录结构所示,展示了location索引(UUID为48G_CgE7TiWomlYsyQW1NQ)的存储信息,共5个primary shard,编号从0-4。

primary shard与replica shard,还有其他几点特性:

  • shard是最小的存储单元,像上面的0,1,2目录,承载部分数据。
  • document是最小的数据单元,只能存在一个primary shard中以及对应的replica shard中(可能有多个),不会拆分存储,也不会存在于多个primary shard里。
  • replica shard是primary shard的数据副本,冗余存储,负责容错,也可以承担查询请求。
  • primary shard不会和自己的replica shard放在一台机器上,否则容错机制就失效了,但是可以和别的replica shard混搭。
  • primary shard的数量在创建索引的时是多少就多少,后续不能改,但replica shard的数量可以随时修改。

 

避免脑裂

脑裂问题是采用master-slave模式的分布式集群普遍需要关注的问题,脑裂一旦出现,会导致集群的状态出现不一致,导致数据错误甚至丢失。

ES避免脑裂的策略:过半原则,可以在ES的集群配置中添加一下配置,避免脑裂的发生

#一个节点多久ping一次,默认1s
discovery.zen.fd.ping_interval: 1s
##等待ping返回时间,默认30s
discovery.zen.fd.ping_timeout: 10s
##ping超时重试次数,默认3次
discovery.zen.fd.ping_retries: 3
##选举时需要的节点连接数,N为具有master资格的节点数量
discovery.zen.minimum_master_nodes=N/2+1

注意问题

  • 配置文件中加入上述避免脑裂的配置,对于网络波动比较大的集群来说,增加ping的时间和ping的次数,一定程度上可以增加集群的稳定性
  • 动态的字段field可能导致元数据暴涨,新增字段mapping映射需要更新mater节点上维护的字段映射信息,master修改了映射信息之后再同步到集群中所有的节点,这个过程中数据的写入是阻塞的。所以建议关闭自动mapping,没有预先定义的字段mapping会写入失败
  • 通过定时任务在集群写入的低峰期,将索引以及mapping映射提前创建好

负载均衡

ES集群是分布式的,数据分布到集群的不同机器上,对于ES中的一个索引来说,ES通过分片的方式实现数据的分布式和负载均衡。创建索引的时候,需要指定分片的数量,分片会均匀的分布到集群的机器中。分片的数量是需要创建索引的时候就需要设置的,而且设置之后不能更改,虽然ES提供了相应的api来缩减和扩增分片,但是代价是很高的,需要重建整个索引。

考虑到并发响应以及后续扩展节点的能力,分片的数量不能太少,假如你只有一个分片,随着索引数据量的增大,后续进行了节点的扩充,但是由于一个分片只能分布在一台机器上,所以集群扩容对于该索引来说没有意义了。

但是分片数量也不能太多,每个分片都相当于一个独立的lucene引擎,太多的分片意味着集群中需要管理的元数据信息增多,master节点有可能成为瓶颈;同时集群中的小文件会增多,内存以及文件句柄的占用量会增大,查询速度也会变慢。

相关配置

5.0之前通过consistency来设置

consistency参数的值可以设为 :

  • one :只要主分片状态ok就允许执行写操作
  • all:必须要主分片和所有副本分片的状态没问题才允许执行写操作
  • quorum:默认值为quorum,即大多数的分片副本状态没问题就允许执行写操作,副本分片数量计算方式为int( (primary +
    number_of_replicas) / 2 ) + 1

5.0之后通过wait_for_active_shards参数设置

  • 索引时增加参数:?wait_for_active_shards=3
  • 给索引增加配置:index.write.wait_for_active_shards=3

容错过程与选举机制

Elasticsearch集群中,所有的node都是对等的角色,所有的node都能接收请求,并且能自动转请求到相应的节点上(数据路由),最后能将其他节点处理的数据进行响应收集,返回给客户端。在集群中,也存在一个master节点,它的职责多一些,需要管理与维护集群的元数据,索引的创建与删除和节点的增加和删除,它都会收到相应的请求,然后进行相应的数据维护。master node在承担索引、搜索请求时,与其他node一起分摊,并不承担所有的请求,因而不存在单点故障这个问题。

我们假设一下集群有3台node,其中node-1宕机的过程,如果node-1是master node,关键步骤如下:

ElasticSearch架构介绍及原理解析_第4张图片

  1. 丢失了3个shard,由于P1丢失,cluster.status瞬间状态变成red。
  2. 重新进行master选举,自动选另一个node作为master。
  3. 新的master将丢失了P1对应的R1(在node-3上面)提升为primary shard ,现全部primary shard active,但是P1,P2的replica shard无法启动,cluster.status变成yellow。
  4. 重启故障的node-1节点,新的master会将缺失的副本都copy一份到node-1上,node-1会使用之间已有的数据,并且同步一下宕机期间的数据修改,此时所有的shard全部active状态,cluster.status重新变成green。

扩容机制

扩容分为垂直扩容和水平扩容两种,垂直扩容指增加单台服务器的CPU、内存大小,磁盘容量,简单来讲就是换更强大的服务器;水平扩容就是增加机器数量,通过集群化部署与分布式的技术手段,也能构建出强大的计算和存储能力。

二者简单对比:

  • 垂直扩容:操作简单,无需要更改集群方案,缺点就是贵,成本呈指数上升,并且单台服务器瓶颈很明显。
  • 水平扩容:业务经常采用,因为更省钱,可以多非常多的普通服务器搭建,缺点是节点数越多,集群内节点之间通信会出现网络拥塞的问题。

Elastisearch非常适合用水平扩容方案,能胜任上百个节点,支撑PB级别的数据规模,并且扩容操作后,每增加新的节点会触发索引分片的重新分配。

举个例子,假定Elasticsearch有2个节点,primary shard设置为3,replica shard设置为1,这样1个索引就有3个primary shard,3个replica shard,P表示primary shard,R表示replica shard,分布示例图如下:

ElasticSearch架构介绍及原理解析_第5张图片

当新加入一个node-3时,触发node-1和node-2的shard进行重新分配,假定P0和R1两个shard移到node-3当中,如图所示:

ElasticSearch架构介绍及原理解析_第6张图片

重分配完成后,此时集群的示例如下:

ElasticSearch架构介绍及原理解析_第7张图片

最后补充两点:

  • 同一个index的primay shard和replica shard不能在同一个机器上,但不同index的primary shard和replica shard可以混搭。
  • 负载均衡也不是完全平均的,有的多有的少,Elasticsearch会根据当前情况自动分配shard。

容错机制

单node环境下的容错

假定Elasticsearch集群只有一个node,primary shard设置为3,replica shard设置为1,这样1个索引就应该有3个primary shard,3个replica shard,但primary shard不能与其replica shard放在一个node里,导致replica shard无法分配,这样集群的status为yellow,示例图如下:

ElasticSearch架构介绍及原理解析_第8张图片

集群可以正常工作,一旦出现node宕机,数据全部丢失,并且集群不可用。

结论:单node环境容错性为0.

2台node环境下的容错

primary shard与replica shard的设置与上文相同,此时Elasticsearch集群只有2个node,shard分布如下图所示:

ElasticSearch架构介绍及原理解析_第9张图片

如果其中一台宕机,如node-2宕机,如图所示:

ElasticSearch架构介绍及原理解析_第10张图片

此时node-1节点的R2(replica shard)会升为P2(primary shard),此时集群还能正常用,数据未丢失。

结论:双node环境容错性为1。

3台node环境下的容错

我们先按primary shard为3,replica shard为1进行容错性计算。

此时每台node存放2个shard,如果一台宕机,此时另外2台肯定还有完整的数据,如果两台宕机,剩下的那台就只有2/3的数据,数据丢失1/3,容错性为1台。

如果是这样设置,那3台的容错性和2台的容错性一样,就存在资源浪费的情况。

那怎么样提升容错性呢?

把replica shard的值改成2,这样每台node存放3个shard,如下图所示:

ElasticSearch架构介绍及原理解析_第11张图片

如果有2台宕机,就剩下node-2,此时集群的数据还是完整的,replica会升成primary shard继续提供服务,如下图所示:

ElasticSearch架构介绍及原理解析_第12张图片

结论:3台node环境容错性最大可以是2。

Lucene

结构原理

ES依赖一个重要的组件Lucene,关于数据结构的优化通常来说是对Lucene的优化,它是集群的一个存储于检索工作单元。


ElasticSearch架构介绍及原理解析_第13张图片

在Lucene中,分为索引(录入)与检索(查询)两部分,索引部分包含分词器、过滤器、字符映射器等,检索部分包含查询解析器等。

一个Lucene索引包含多个segments,一个segment包含多个文档,每个文档包含多个字段,每个字段经过分词后形成一个或多个term。

通过Luke工具查看ES的lucene文件如下,主要增加了_id和_source字段:

ElasticSearch架构介绍及原理解析_第14张图片

Lucene索引实现

Lucene 索引文件结构主要的分为:词典、倒排表、正向文件、DocValues等,如下图:

ElasticSearch架构介绍及原理解析_第15张图片

ElasticSearch架构介绍及原理解析_第16张图片

Lucene随机三次磁盘读取比较耗时。其中.fdt文件保存数据值损耗空间大,.tim和.doc则需要SSD存储提高随机读写性能。另外一个比较消耗性能的是打分流程,不需要则可屏蔽。

DocValues

倒排索引解决从词快速检索到相应文档ID, 但如果需要对结果进行排序、分组、聚合等操作的时候则需要根据文档ID快速找到对应的值。

通过倒排索引代价缺很高:需迭代索引里的每个词项并收集文档的列里面 token。这很慢而且难以扩展:随着词项和文档的数量增加,执行时间也会增加。Solr docs对此的解释如下:

在lucene 4.0版本前通过FieldCache,原理是通过按列逆转倒排表将(field value ->doc)映射变成(doc -> field value)映射,问题为逐步构建时间长并且消耗大量内存,容易造成OOM。

DocValues是一种列存储结构,能快速通过文档ID找到相关需要排序的字段。在ES中,默认开启所有(除了标记需analyzed的字符串字段)字段的doc values,如果不需要对此字段做任何排序等工作,则可关闭以减少资源消耗。

关于ES索引与检索分片

ES中一个索引由一个或多个lucene索引构成,一个lucene索引由一个或多个segment构成,其中segment是最小的检索域。

数据具体被存储到哪个分片上:shard = hash(routing) % number_of_primary_shards

默认情况下 routing参数是文档ID (murmurhash3),可通过 URL中的 _routing 参数指定数据分布在同一个分片中,index和search的时候都需要一致才能找到数据,如果能明确根据_routing进行数据分区,则可减少分片的检索工作,以提高性能。

运行原理解析

倒排索引说明

参考:https://segmentfault.com/a/1190000020022504

检索倒排索引

先对检索内容进行分词(适用于match查询方法,term/terms查询不分词),然后在倒排索引中寻找匹配的token,最后返回token对应的文档以及根据文档中token匹配情况给出的评分score。

分词器Analyzer

在Elasticsearch中可通过内置分词器实现分词,也可以按需定制分词器。

Analyzer由三部分组成:

  • Character Filters:原始文本处理,它的作用是整理字符串。如去除HTML,或者将&转化为and;
  • Tokenizer:按照规则切分为单词。比如whitespace的分词器在遇到空格和标点的时候,可能会将文本进行拆分;
  • Token Filters:对切分单词加工,如小写(lowercase token filter)、删除a,and和the等stopwords(stop token filter),增加同义词(synonym token filter)等。

利用ElasticSearch提供的webAPI可以查看索引中某一字段field对文本text的分词策略:

POST /[index]/_analyze
{
  "field": [field],
  "text": [text]
}

建立索引和类型

  • 在Elasticsearch集群中,节点是对等的,节点间会通过自己的一些规则选取集群的Master,Master节点会负责集群状态信息的改变,并同步给其他节点。
  • 建立索引和类型的请求先发送到Master节点,Master建立完索引后,将集群状态同步至Slave节点。
  • 只有建立索引和类型需要经过Master节点,数据的写入有一个简单的Routing规则,可以Route到集群中的任意节点,所以数据写入压力是分散在整个集群的。

分片内文档写入流程场景

ElasticSearch架构介绍及原理解析_第17张图片

  • In-memory Buffer
    在主分片节点上,文档会先被写入到内存(In-memory Buffer)中,此时数据还不能被搜索到。
  • Refresh
    经过一段时间(refresh_interval)或者内存缓冲满了,Elasticsearch会将内存中的文档Refresh到文件系统缓存(Filesystem Cache)中,并清除内存中的对应文档。
  • Filesystem Cache
    文档在文件系统缓存中被解析为Lucene的底层文件Segment中,同时建立倒排索引。这个时候文档是可以被搜索到的,因此减少了后续写入磁盘的大量时间,体现了Elasticsearch搜索的实时性(下图中的绿色和灰色图标分别代表已写入磁盘和未写入磁盘的文档)。
    ElasticSearch架构介绍及原理解析_第18张图片
  • Segment
    Lucene把每次生成的倒排索引,叫做一个段(Segment),它无法被修改,只能被合并和删除。另外使用一个Commit文件,记录索引内所有的Segment。由于每次打开一个Segment就会消耗一个文件句柄,随着Segment越来越多,将导致查询性能越来越差。这时,ElasticSearch后台会有一个单独线程专门合并Segment,将零碎的小的Segment合并成一个大的Segment。
    ElasticSearch架构介绍及原理解析_第19张图片
    ElasticSearch架构介绍及原理解析_第20张图片
  • Flush
    将Segment写入磁盘中,更新Commit文件并删除文档对应的Translog文件。
  • Translog
    Elasticsearch在把数据写入到Index Buffer的同时,其实还另外记录了一个Translog日志。它是以顺序写文件的形式写入到磁盘中的,速度较快。如果发生异常,Elasticsearch会从Commit位置开始,恢复整个Translog文件中的记录,保证数据一致性。

多个分片的文档写入场景

确定文档存储位置

计算方式:

shard = hash(routing) % number_of_primary_shards

每个文档都有一个routing参数,默认情况下就使用其 _id 值。将其 _id 值计算哈希后,对索引的主分片数取余,就得到了文档实际应该存储到的分片。因此索引的主分片数不可以随意修改,一旦主分片数改变,所有文档的存储位置计算结果都会发生改变,索引数据就完全不可读了。

同步副本

ElasticSearch架构介绍及原理解析_第21张图片

  1. 客户端请求发送给Node 1节点,注意图中Node 1是Master节点,实际完全可以不是;
  2. Node 1用文档的 _id 取余计算得到应该将数据存储到shard 0上。通过Cluster State信息发现shard 0的主分片已经分配到了Node 3上。Node 1转发请求数据给Node 3;
  3. Node 3完成请求数据的索引过程,存入主分片 0。然后并行转发数据给分配有shard 0的副本分片的Node 1和Node 2。当收到任一节点汇报副本分片数据写入成功,Node 3即返回给初始的接收节点 Node 1,宣布数据写入成功。Node 1返回成功响应给客户端;
  4. 当集群中某个节点宕机,该节点上所有分片中的数据全部丢失(既有主分片,又有副分片)。丢失的副分片对数据的完整性没有影响,丢失的主分片在其他节点上的副分片会被选举成主分片;所以整个索引的数据完整性没有被破坏。

注:图中P代表主分片(Primary Shard),R代表副本(Replica Shard)。

根据_id查询文档场景

GET /[index]/_doc/[_id]

ElasticSearch架构介绍及原理解析_第22张图片

  1. Elasticsearch集群中的任意节点都可以作为协调(Coordinating)节点接受请求,每个节点都知道集群中任一文档位置;
  2. 协调节点对文档的_id进行路由,从而判断该数据在哪个Shard,然后将请求转发给对应的节点,此时会使用随机轮询算法,在Primary Shard和Replica Shard中随机选择一个,从而对请求负载均衡;
  3. 处理请求的节点返回文档给协调节点;
  4. 协调节点返回文档给客户端。

根据字段值检索数据场景

GET /[index]/_search?q=[field]: [value]

ElasticSearch架构介绍及原理解析_第23张图片

  1. Elasticsearch集群中的任意节点都可以作为协调(Coordinating)节点接受请求,每个节点都知道集群中任一文档位置;
  2. 协调节点进行分词等操作后,向所有的shard节点发送检索请求;
  3. ElasticSearch已建立字段的倒排索引,即可通过字段值检索到所在文档的_id。随后Shard将满足条件的数据(_id、排序字段等)信息返回给协调节点;
  4. 协调节点将数据重新进行排序,获取到真正需要返回的文档的_id。协调节点再次向对应的Shard发起请求(此时已经有_id 了,可以直接定位到对应的Shard);
  5. Shard将_id对应的文档的完整内容返回给协调节点;
  6. 协调节点获取到全部检索结果,返回给客户端。

上述流程和根据_id查询文档相比,只是多了一个从倒排索引中根据字段值寻找文档_id的过程,其中的4~6步与其完全相同。

调优

可参考:https://www.amd5.cn/atang_4784.html

分片的数量

  • 每个节点的分片数量保持在低于每1GB堆内存对应集群的分片在20-25之间。
  • 分片大小为50GB通常被界定为适用于各种用例的限制。

JVM设置

  • 堆内存设置:不要超过32G,在Java中,对象实例都分配在堆上,并通过一个指针进行引用。对于64位操作系统而言,默认使用64位指针,指针本身对于空间的占用很大,Java使用一个叫作内存指针压缩(compressed
    oops)的技术来解决这个问题,简单理解,使用32位指针也可以对对象进行引用,但是一旦堆内存超过32G,这个压缩技术不再生效,实际上失去了更多的内存。
  • 预留一半内存空间给lucene用,lucene会使用大量的堆外内存空间。
  • 如果你有一台128G的机器,一半内存也是64G,超过了32G,可以通过一台机器上启动多个ES实例来保证ES的堆内存小于32G。
  • ES的配置文件中加入bootstrap.mlockall: true,关闭内存交换。

通过_cat api获取任务执行情况

GET http://localhost:9201/_cat/thread_pool?v&h=host,search.active,search.rejected,search.completed
  • 完成(completed)
  • 进行中(active)
  • 被拒绝(rejected):需要特别注意,说明已经出现查询请求被拒绝的情况,可能是线程池大小配置的太小,也可能是集群性能瓶颈,需要扩容。

小技巧

  • 重建索引或者批量想ES写历史数据的时候,写之前先关闭副本,写入完成之后,再开启副本。
  • ES默认用文档id进行路由,所以通过文档id进行查询会更快,因为能直接定位到文档所在的分片,否则需要查询所有的分片。
  • 使用ES自己生成的文档id写入更快,因为ES不需要验证一次自定义的文档id是否存在。

开发说明

  • JAVA API接口

http://www.ibm.com/developerworks/library/j-use-elasticsearch-java-apps/index.html

  • 常见的增、删、改、查操作实现样例: 

http://blog.csdn.net/laoyang360/article/details/51931981

  • ES官方网站

https://discuss.elastic.co/

http://elasticsearch.cn/

 

参考文献

https://segmentfault.com/a/1190000020022504

https://www.amd5.cn/atang_4784.html

https://segmentfault.com/a/1190000021091902

https://qimok.cn/1360.html

 

你可能感兴趣的:(分布式日志管理系统,elk,elasticsearch)