Elasticsearch 简介

Elasticsearch 简介

Elasticsearch 是一个基于 Lucene 的搜索和数据分析工具,它提供了一个分布式服务。Elasticsearch 是遵从 Apache 开源条款的一款开源产品,是当前主流的企业级搜索引擎。

它用于全文搜索、结构化搜索、分析以及将这三者混合使用:

  • 维基百科使用 Elasticsearch 提供全文搜索并高亮关键字,以及**输入实时搜索(search-as-you-type)搜索纠错(did-you-mean)**等搜索建议功能。
  • 英国卫报使用 Elasticsearch 结合用户日志和社交网络数据提供给他们的编辑以实时的反馈,以便及时了解公众对新发表的文章的回应。
  • StackOverflow 结合全文搜索与地理位置查询,以及more-like-this功能来找到相关的问题和答案。
  • Github 使用 Elasticsearch 检索 1300 亿行的代码。

Elasticsearch 特点

  • 分布式的实时文件存储,每个字段都被索引并可被搜索;
  • 分布式的实时分析搜索引擎;
  • 可弹性扩展到上百台服务器规模,处理 PB 级结构化或非结构化数据;
  • 开箱即用(安装即可使用),它提供了许多合理的缺省值,并对初学者隐藏了复杂的搜索引擎理论。只需很少的学习既可在生产环境中使用。

Elasticsearch 发展历史

  • 2010 年 2 月 8 日,Elasticsearch 第一个公开版本发布。

  • 2010 年 5 月 14 日,发布第一个具有里程碑意义的初始版本 0.7.0 ,具有如下特征:

  • Zen Discovery 自动发现模块;

    • 支持 Groovy Client;
  • 简单的插件管理机制;

    • 更好地支持 icu 分词器;
  • 更多的管理 api。

  • 2013 年初,GitHub 抛弃了 Solr,采取 ElasticSearch 来做其 PB 级的搜索。

  • 2014 年 2 月 14 日,发布 1.0.0 版本,增加如下重要特性:

  • 支持 Snapshot/Restore API 备份恢复 API;

    • 支持聚合分析 Aggregations;
  • 支持 cat api;

    • 支持断路器;
  • 引入 Doc values。

  • 2015 年 10 月 28 日,发布 2.0.0 版本,有如下重要特性:

  • 增加了 Pipleline Aggregations;

    • query/filter 查询合并,都合并到 query 中,根据不同的上下文执行不同的查询;
  • 压缩存储可配置;

    • Rivers 模块被移除;
  • Multicast 组播发现被移除,成为一个插件,生产环境必须配置单播地址。

  • 2016 年 10 月 26 日,发布 5.0.0 版本,有如下重大特性变化:

  • Lucene 6.x 的支持,磁盘空间少一半;索引时间少一半;查询性能提升 25%;支持 IPV6;

    • Internal Engine 级别移除了用于避免同一文档并发更新的竞争锁,带来 15%-20% 的性能提升;
  • Shrink API,它可将分片数进行收缩成它的因数,如之前你是 15 个分片,你可以收缩成 5 个或者 3 个又或者 1 个,那么我们就可以想象成这样一种场景,在写入压力非常大的收集阶段,设置足够多的索引,充分利用 shard 的并行写能力,索引写完之后收缩成更少的 shard,提高查询性能;

    • 提供了第一个 Java 原生的 REST 客户端 SDK;
  • IngestNode,之前如果需要对数据进行加工,都是在索引之前进行处理,比如 logstash 可以对日志进行结构化和转换,现在直接在 es 就可以处理了;

    • 提供了 Painless 脚本,代替 Groovy 脚本;
    • 移除 site plugins,就是说 head、bigdesk 都不能直接装 es 里面了,不过可以部署独立站点(反正都是静态文件)或开发 kibana 插件;
    • 新增 Sliced Scroll 类型,现在 Scroll 接口可以并发来进行数据遍历了。每个 Scroll 请求,可以分成多个 Slice 请求,可以理解为切片,各 Slice 独立并行,利用 Scroll 重建或者遍历要快很多倍;
    • 新增了 Profile API;
    • 新增了 Rollover API;
    • 新增 Reindex;
    • 引入新的字段类型 Text/Keyword 来替换 String;
    • 限制索引请求大小,避免大量并发请求压垮 ES;
    • 限制单个请求的 shards 数量,默认 1000 个。
  • 2017 年 8 月 31 日,发布 6.0.0 版本,具有如下重要特性:

  • 稀疏性 Doc Values 的支持;

    • Index Sorting,即索引阶段的排序;
  • 顺序号的支持,每个 es 的操作都有一个顺序编号(类似增量设计);

    • 无缝滚动升级;
  • 从 6.0 开始不支持一个 index 里面存在多个 type;

    • Index-template inheritance,索引版本的继承,目前索引模板是所有匹配的都会合并,这样会造成索引模板有一些冲突问题, 6.0 将会只匹配一个,索引创建时也会进行验证;
    • Load aware shard routing, 基于负载的请求路由,目前的搜索请求是全节点轮询,那么性能最慢的节点往往会造成整体的延迟增加,新的实现方式将基于队列的耗费时间自动调节队列长度,负载高的节点的队列长度将减少,让其他节点分摊更多的压力,搜索和索引都将基于这种机制;
    • 已经关闭的索引将也支持 replica 的自动处理,确保数据可靠。
  • 2019 年 4 月 10 日,发布 7.0.0 版本,具有如下重要特性:

  • 集群连接变化:TransportClient 被废弃,es7 的 java 代码,只能使用 restclient;对于 java 编程,建议采用 High-level-rest-client 的方式操作 ES 集群;

    • ES 程序包默认打包 jdk:7.x 版本的程序包大小变成 300MB+,对比 6.x,包大了 200MB+,这正是 JDK 的大小;
  • 采用基于 Lucene 9.0;

    • 正式废除单个索引下多 Type 的支持,es6 时,官方就提到了 es7 会删除 type,并且 es6 时,已经规定每一个 index 只能有一个 type。在 es7 中,使用默认的 _doc 作为 type,官方说在 8.x 版本会彻底移除 type。api 请求方式也发送变化,如获得某索引的某 ID 的文档:GET index/_doc/id 其中 index 和 id 为具体的值;
  • 引入了真正的内存断路器,它可以更精准地检测出无法处理的请求,并防止它们使单个节点不稳定;

    • Zen2 是 Elasticsearch 的全新集群协调层,提高了可靠性、性能和用户体验,变得更快、更安全,并更易于使用。

Elasticsearch 概念

下列有一些概念是 Elasticsearch 的核心。从一开始就理解这些概念将极大地帮助简化学习 Elasticsearch 的过程。

近实时(NRT)

Elasticsearch 是一个近乎实时的搜索平台。这意味着从索引文档到可搜索文档的时间有一点延迟(通常是一秒)。

索引(Index)

索引在不同语境,有着不同的含义

  • 索引(名词):一个 索引 类似于传统关系数据库中的一个 数据库 ,是一个存储关系型文档的容器。 索引 (index) 的复数词为 indices 或 indexes 。索引实际上是指向一个或者多个物理分片逻辑命名空间
  • 索引(动词):索引一个文档 就是存储一个文档到一个 索引 (名词)中以便被检索和查询。这非常类似于 SQL 语句中的 INSERT 关键词,除了文档已存在时,新文档会替换旧文档情况之外。
  • 倒排索引:关系型数据库通过增加一个索引比如一个 B 树索引到指定的列上,以便提升数据检索速度。Elasticsearch 和 Lucene 使用了一个叫做 倒排索引 的结构来达到相同的目的。

索引的 Mapping 和 Setting

  • Mapping 定义文档字段的类型
  • Setting 定义不同的数据分布

示例:

1
2
3
4
5
6
7
8
{
"settings": { ... any settings ... },
"mappings": {
"type_one": { ... any mappings ... },
"type_two": { ... any mappings ... },
...
}
}

倒排索引

img

index template

**index template**(索引模板)帮助用户设定 Mapping 和 Setting,并按照一定的规则,自动匹配到新创建的索引之上。

  • 模板仅在一个索引被创建时,才会产生作用。修改模板不会影响已创建的索引。
  • 你可以设定多个索引模板,这些设置会被 merge 在一起。
  • 你可以指定 order 的数值,控制 merge 的过程。

当新建一个索引时

  • 应用 ES 默认的 Mapping 和 Setting
  • 应用 order 数值低的 index template 中的设定
  • 应用 order 数值高的 index template 中的设定,之前的设定会被覆盖
  • 应用创建索引是,用户所指定的 Mapping 和 Setting,并覆盖之前模板中的设定。

示例:创建默认索引模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
PUT _template/template_default
{
"index_patterns": ["*"],
"order": 0,
"version": 1,
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
}
}

PUT /_template/template_test
{
"index_patterns": ["test*"],
"order": 1,
"settings": {
"number_of_shards": 1,
"number_of_replicas": 2
},
"mappings": {
"date_detection": false,
"numeric_detection": true
}
}

# 查看索引模板
GET /_template/template_default
GET /_template/temp*

#写入新的数据,index以test开头
PUT testtemplate/_doc/1
{
"someNumber": "1",
"someDate": "2019/01/01"
}
GET testtemplate/_mapping
GET testtemplate/_settings

PUT testmy
{
"settings":{
"number_of_replicas":5
}
}

PUT testmy/_doc/1
{
"key": "value"
}

GET testmy/_settings
DELETE testmy
DELETE /_template/template_default
DELETE /_template/template_test

dynamic template

  • 根据 ES 识别的数据类型,结合字段名称,来动态设定字段类型
    • 所有的字符串类型都设定成 Keyword,或者关闭 keyword 字段。
    • is 开头的字段都设置成 boolean
    • long_ 开头的都设置成 long 类型
  • dynamic template 是定义在某个索引的 Mapping 中
  • template 有一个名称
  • 匹配规则是一个数组
  • 为匹配到字段设置 Mapping

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#Dynaminc Mapping 根据类型和字段名
DELETE my_index

PUT my_index/_doc/1
{
"firstName": "Ruan",
"isVIP": "true"
}

GET my_index/_mapping

DELETE my_index
PUT my_index
{
"mappings": {
"dynamic_templates": [
{
"strings_as_boolean": {
"match_mapping_type": "string",
"match": "is*",
"mapping": {
"type": "boolean"
}
}
},
{
"strings_as_keywords": {
"match_mapping_type": "string",
"mapping": {
"type": "keyword"
}
}
}
]
}
}
GET my_index/_mapping

DELETE my_index
#结合路径
PUT my_index
{
"mappings": {
"dynamic_templates": [
{
"full_name": {
"path_match": "name.*",
"path_unmatch": "*.middle",
"mapping": {
"type": "text",
"copy_to": "full_name"
}
}
}
]
}
}
GET my_index/_mapping


PUT my_index/_doc/1
{
"name": {
"first": "John",
"middle": "Winston",
"last": "Lennon"
}
}

GET my_index/_search?q=full_name:John
DELETE my_index

类型(Type)

type 是一个逻辑意义上的分类或者叫分区,允许在同一索引中建立多个 type。本质是相当于一个过滤条件,高版本将会废弃 type 概念。

6.0.0 版本及之后,废弃 type

文档(Document)

Elasticsearch 是面向文档的,文档是所有可搜索数据的最小单位

Elasticsearch 使用 JSON 作为文档的序列化格式。

在索引/类型中,可以根据需要存储任意数量的文档。

每个文档都有一个 Unique ID

  • 用户可以自己指定
  • 或通过 Elasticsearch 自动生成

文档的元数据

一个文档不仅仅包含它的数据 ,也包含元数据 —— 有关文档的信息。

  • _index:文档在哪存放
  • _type:文档表示的对象类别
  • _id:文档唯一标识
  • _source:文档的原始 Json 数据
  • _all:整合所有字段内容到该字段,已被废除
  • _version:文档的版本信息
  • _score:相关性打分

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"_index": "megacorp",
"_type": "employee",
"_id": "1",
"_version": 1,
"found": true,
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": ["sports", "music"]
}
}

节点(Node)

节点简介

一个运行中的 Elasticsearch 实例称为一个节点

Elasticsearch 实例本质上是一个 Java 进程。一台机器上可以运行多个 Elasticsearch 进程,但是生产环境建议一台机器上只运行一个 Elasticsearch 进程

每个节点都有名字,通过配置文件配置,或启动时通过 -E node.name=node1 指定。

每个节点在启动后,会分配一个 UID,保存在 data 目录下。

节点类型

  • 主节点(master node):每个节点都保存了集群的状态,只有 master 节点才能修改集群的状态信息(保证数据一致性)。集群状态,维护了以下信息:
    • 所有的节点信息
    • 所有的索引和其相关的 mapping 和 setting 信息
    • 分片的路由信息
  • 候选节点(master eligible node):master eligible 节点可以参加选主流程。第一个启动的节点,会将自己选举为 mater 节点。
    • 每个节点启动后,默认为 master eligible 节点,可以通过配置 node.master: false 禁止
  • 数据节点(data node):负责保存分片数据。
  • 协调节点(coordinating node):负责接收客户端的请求,将请求分发到合适的接地那,最终把结果汇集到一起。每个 Elasticsearch 节点默认都是协调节点(coordinating node)。
  • 冷/热节点(warm/hot node):针对不同硬件配置的数据节点(data node),用来实现 Hot & Warm 架构,降低集群部署的成本。
  • 机器学习节点(machine learning node):负责执行机器学习的 Job,用来做异常检测。

节点配置

配置参数 默认值 说明
node.master true 是否为主节点
node.data true 是否为数据节点
node.ingest true
node.ml true 是否为机器学习节点(需要开启 x-pack)

建议

开发环境中一个节点可以承担多种角色。但是,在生产环境中,节点应该设置为单一角色。

集群(Cluster)

集群简介

拥有相同 cluster.name 配置的 Elasticsearch 节点组成一个集群cluster.name 默认名为 elasticsearch,可以通过配置文件修改,或启动时通过 -E cluster.name=xxx 指定。

当有节点加入集群中或者从集群中移除节点时,集群将会重新平均分布所有的数据。

当一个节点被选举成为主节点时,它将负责管理集群范围内的所有变更,例如增加、删除索引,或者增加、删除节点等。 而主节点并不需要涉及到文档级别的变更和搜索等操作,所以当集群只拥有一个主节点的情况下,即使流量增加,它也不会成为瓶颈。 任何节点都可以成为主节点。

作为用户,我们可以将请求发送到集群中的任何节点 ,包括主节点。 每个节点都知道任意文档所处的位置,并且能够将我们的请求直接转发到存储我们所需文档的节点。 无论我们将请求发送到哪个节点,它都能负责从各个包含我们所需文档的节点收集回数据,并将最终结果返回給客户端。 Elasticsearch 对这一切的管理都是透明的。

集群健康

Elasticsearch 的集群监控信息中包含了许多的统计数据,其中最为重要的一项就是 集群健康 , 它在 status 字段中展示为 greenyellow 或者 red

在一个不包含任何索引的空集群中,它将会有一个类似于如下所示的返回内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"cluster_name" : "elasticsearch",
"status" : "green",
"timed_out" : false,
"number_of_nodes" : 1,
"number_of_data_nodes" : 1,
"active_primary_shards" : 5,
"active_shards" : 5,
"relocating_shards" : 0,
"initializing_shards" : 0,
"unassigned_shards" : 0,
"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" : 100.0
}

status 字段指示着当前集群在总体上是否工作正常。它的三种颜色含义如下:

  • **green**:所有的主分片和副本分片都正常运行。
  • **yellow**:所有的主分片都正常运行,但不是所有的副本分片都正常运行。
  • **red**:有主分片没能正常运行。

分片(Shards)

分片简介

索引实际上是指向一个或者多个物理分片逻辑命名空间

一个分片是一个底层的工作单元 ,它仅保存了全部数据中的一部分。一个分片可以视为一个 Lucene 的实例,并且它本身就是一个完整的搜索引擎。 我们的文档被存储和索引到分片内,但是应用程序是直接与索引而不是与分片进行交互。

Elasticsearch 是利用分片将数据分发到集群内各处的。分片是数据的容器,文档保存在分片内,分片又被分配到集群内的各个节点里。 当你的集群规模扩大或者缩小时, Elasticsearch 会自动的在各节点中迁移分片,使得数据仍然均匀分布在集群里。

主分片和副分片

分片分为主分片(Primary Shard)和副分片(Replica Shard)。

主分片:用于解决数据水平扩展的问题。通过主分片,可以将数据分布到集群内不同节点上。

  • 索引内任意一个文档都归属于一个主分片。
  • 主分片数在索引创建时指定,后序不允许修改,除非 Reindex

副分片(Replica Shard):用于解决数据高可用的问题。副分片是主分片的拷贝。副本分片作为硬件故障时保护数据不丢失的冗余备份,并为搜索和返回文档等读操作提供服务。

  • 副分片数可以动态调整
  • 增加副本数,还可以在一定程度上提高服务的可用性(读取的吞吐)

对于生产环境中分片的设定,需要提前做好容量规划

分片数过小

  • 无法水平扩展
  • 单个分片的数量太大,导致数据重新分配耗时

分片数过大

  • 影响搜索结果的相关性打分,影响统计结果的准确性
  • 单节点上过多的分片,会导致资源浪费,同时也会影响性能

副本(Replicas)

副本主要是针对主分片(Shards)的复制,Elasticsearch 中主分片可以拥有 0 个或多个的副本。

副本分片的主要目的就是为了故障转移。

分片副本很重要,主要有两个原因:

  • 它在分片或节点发生故障时提供高可用性。因此,副本分片永远不会在与其复制的主分片相同的节点;
  • 副本分片也可以接受搜索的请求,可以并行搜索,从而提高系统的吞吐量。

每个 Elasticsearch 分片都是 Lucene 索引。单个 Lucene 索引中可以包含最大数量的文档。截止 LUCENE-5843,限制是 2,147,483,519(= Integer.MAX_VALUE - 128)文档。您可以使用_cat/shardsAPI 监控分片大小。

参考资料