ElasticSearch入门

ElasticSearch

ES

ES 是一个开源的高扩展的分布式全文搜索引擎,是整个 Elastic Stack 技术栈的核心,它可以近乎实时的存储、检索数据;本身的扩展性很好,可以扩展到上百台服务器,处理 PB 级别的数据。

ElasticSearch 与 MySQL 类型对比

Elasticsearch 是面向文档型数据库,一条数据在这里就是一个文档。

ElasticSearch MySQL
Index Database
Type Table
Documents Row
Fields Column

ES 里的 Index 可以看作一个库,而 Types 相当于表,Documents 则相当于表的行。

Types 的概念已经被逐渐弱化,ElasticSearch 6.X 中,一个 index 下已经只能包含一个 type,ElasticSearch 7.X 中,Type 的概念已经被删除了。

windows 上 elasticsearch 集群搭建

修改 config 下的 elasticsearch.yml 文件

1
2
3
4
5
6
7
8
9
10
11
12
cluster.name: my-application # 所有节点的集群名称要一致
node.name: node-1001 # 不同节点的名称要求不一致
node.master: true
node.data: true
network.host: 127.0.0.1
http.port: 1001
transport.tcp.port: 9301 # 添加通信端口

# 非主节点添加
discovery.seed_hosts: ["127.0.0.1:9301"] # 主节点地址
discovery.zen.fd.ping_timeout: 1m
discovery.zen.fd.ping_retries: 5

ES 进阶

1、索引(Index)

一个索引就是一个拥有几分相似特征的文档的集合。比如说,可以有一个客户数据的索引,另一个产品目录的索引,还有一个订单数据的索引。一个索引由一个名字来标识(必须全部是小写字母),并且当我们要对这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字,在一个集群中,可以定义任意多的索引。

Elasticsearch 索引的精髓:一切设计都是为了提高搜索的性能

2、类型(Type)

在一个索引中,可以定义一种或多种类型。

一个类型是索引的一个逻辑上的分类/分区,其语义由自己决定,通常,会为具有一组共同字段的文档定义一个类型。

3、文档(Document)

一个文档是一个可被索引的基础信息单元,也就是一条数据

在一个 index/type 里面,可以存储任意多的文档

4、字段(Field)

相当于是数据表的字段,对文档数据根据不同属性进行的分类标识

5、映射(Mapping)

mapping 是处理数据的方式和规则方面做一些限制,如某个字段的数据类型、默认值、分析器、是否被索引等等。

6、分片(Shards)

一个索引可以存储超出单个节点硬件限制的大量数据。ES 提供了将索引划分成多份的能力,每一份就称之为分片,当创建一个索引时,可以指定想要的分片的数量,每个分片本身也是一个功能完善并且独立的”索引“,这个”索引“可以被放置到集群中任何节点上。

  1. 允许水平分割 / 扩展内容容量
  2. 允许在分片之上进行分布式的、并行的操作,进而提高性能 / 吞吐量

7、副本(Replicas)

在一个网络 / 云的环境里,失败随时都可能发生,在某个分片 / 节点不知怎么的就处于离线状态,或者由于任何原因消失了,在这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的,ES 允许用户创建分片的一份或多份拷贝,这些拷贝叫做复制分片(副本)

  1. 在分片 / 节点失败的情况下,提高了可用性,因为这个原因,注意到复制分片从不与原 / 主要(original/primary)分片置于同一节点上是非常重要的
  2. 扩展搜索量 / 吞吐量,因为搜索可以在所有的副本上并行运行

8、分配(Allocation)

将分片分配给某个节点的过程,包括分配主分片或者副本。如果是副本,还包含从主分片复制数据的过程。这个过程是由 master 节点完成的。

分布式集群

9、单节点集群

在包含一个空节点的集群内创建名为 users 的索引,分配三个主分片和一个副本(每个主分片拥有一个副本分片)

1
2
3
4
5
6
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}

10、路由计算

1
路由计算:hash(id) % 主分片数量 = [0,1,2]

11、分片控制

1
用户可以访问任何一个节点获取数据,这个节点称之为协调节点

倒排索引

12、词条

索引中最小存储和查询单元

13、词典

词条的集合,B+ 树,HashMap

14、倒排表

关键词出现的位置,关键词出现的频率,存储词条的指针

文档分析

分析包含下面的过程:

  • 将一块文本分成适合于倒排索引的独立的词条

  • 将这些词条统一化为标准格式以提高它们的”可搜索性“,或者 recall 分析器执行上面的工作,分析器实际上是将三个功能封装到了一个包里:

    • 字符过滤器:

      首先,字符串按顺序通过每个字符过滤器,它们的任务是在分词前整理字符串,一个字符过滤器可以用来去掉 HTML,或者将 & 转为 and。

    • 分词器

      其次,字符串被分词器分为单个词条,一个简单的分词器遇到空格和标点的时候,可能会将文本拆分为词条。

    • Token 过滤器

      最后,词条按顺序通过每个 token 过滤器,这个过程可能会改变词条(例如,小写化 Quick),删除词条(例如,像 a、and、the 等无用词),或者增加词条(例如,像 jump 和 leap 这种同义词)。

内置分析器

例如:

1
Set the shape to semi-transparent by calling set_trans(5)
  • 标准分析器

    是 es 默认使用的分析器,是分析各种语言文本最常用的选择,是根据 unicode 联盟定义的单词边界划分文本,删除绝大部分标点。最后将词条小写。

    它将产生:

    1
    set,the,shape,to,semi,transparent,by,calling,set_trans,5
  • 简单分析器

    在任何不是字母的地方分隔文本,将词条小写

    它将产生:

    1
    set,the,shape,to,semi,transparent,by,calling,set,trans
  • 空格分析器

    空格分析器在空格的地方划分文本

    它将产生:

    1
    Set,the,shaoe,to,semi-transparent,by,calling,set_trans(5)
  • 语言分析器

    特点语言分析器可用于很多语言,它们可以考虑指定语言的特点。例如英语分析器附带了一组英语无用词(常用单词,例如 and 或者 the,它们对于相关性没有什么影响),它们会被删除,由于理解英语语法的规则,这个分词器可以提取英语单词的词干。

    它将产生:

    1
    2
    3
    set,shape,semi,transpar,call,set_tral,5

    transparent,calling,set_trans 已经变为了词根模式

IK 分词器

对中文分词的支持

要与 es 的版本对应

Releases · medcl/elasticsearch-analysis-ik · GitHub

1
2
3
4
{
"analyzer":"ik_max_word", // 最细粒度的分词 , ik_smart 最粗粒度的分词
"text":"测试单词"
}

自定义词条

ES 根目录中的 plugins 文件夹下的 ik 文件夹,进入 config 目录,创建 custom.dic 文件,放入自定义词条。打开 IkAnalyzer.cfg.xml 文件,将新建的 custom.cfg 配置其中,重启 ES 服务器。

自定义分析器

一个分析器就是在一个包里面组合了三种函数的一个包装器,三种函数按照顺序被执行:

  • 字符过滤器

    用来整理一个尚未被分词的字符串。例如,如果文本是 HTML 格式的,它会包含像

    或者

    这样的 HTML 标签,这些标签不是我们想索引的,可以使用 HTML 清除字符过滤器,来移除所有的 HTML 标签,一个分析器可能有 0 个或者多个字符过滤器。

  • 分词器

    一个分析器必须有一个唯一的分词器,分词器把字符串分解成单个词条或词汇单元,标准分析器里使用的标准分词器把一个字符串根据单词边界分解成单个词条,并且移除大部分的标点符号。

  • 词单元过滤器

    经过分词,作为结果的词单元流会按照指定的顺序通过指定的词单元过滤器。词单元过滤器可以修改、添加或者移除词单元。

创建自定义的分析器

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
//PUT localhost:9200/my_index
{
"settings": {
"analysis": {
"char_filter": {
"&_to_and": {
"type": "mapping",
"mappings": [
"&=>and"
]
}
},
"filter": {
"my_stopwords": {
"type": "stop",
"stopwords": [
"the",
"a"
]
}
},
"analyzer": {
"my_analyzer": {
"type": "custom",
"char_filter": [
"html_strip",
"&_to_and"
],
"tokenizer": "standard",
"filter": [
"lowercase",
"my_stopwords"
]
}
}
}
}
}

文档处理

15、文档冲突

当使用 index API 更新文档,可以一次性读取原始文档,做我们的修改,然后重新索引整个文档,最近的请求索引将获胜:无论最后哪一个文档被索引,都将被唯一存储在 Elasticsearch 中,如果其他人同时更改这个文档,他们的更改将丢失。

  • 悲观并发控制

    这种方法被关系型数据库广泛使用,它假定有变更冲突可能发生,因此阻塞访问资源以防止冲突。一个典型的例子就是读取一行数据之前先将其锁住,确保只有放置锁的线程能够对这行数据进行修改。

  • 乐观并发控制

    Elasticsearch 中使用的这种方法假定冲突是不可能发生的,并且不会阻塞正在尝试的操作。然而,如果源数据在读写当中被修改,更新将会失败。应用程序接下来将决定该如何解决冲突。例如,可以重试更新、使用新的数据、或者将相关情况报告给用户。

16、外部系统版本控制

一个常见的设置是使用其它数据库作为主要的数据存储,使用 Elasticsearch 做数据检索,这意味着主数据库的所有更改发生时都需要被复制到 Elasticsearch,如果多个进程负责这一数据同步,可以遇到并发问题。

如果主数据库有了版本号,或一个能作为版本号的字段比如 timestamp,那么可以在 Elasticsearch 中通过增加 version_type=external 到查询字符串的方式重用这些相同的版本号,版本号必须是大于零的整数,且小于 9.2E+18 ——一个 java 中的 long 类型的正值。

Kibana

是一个免费且开放的用户界面,能够让你对 Elasticsearch 数据进行可视化,并让你在 Elastic Stack 中进行导航,从跟踪查询负载,到理解请求如何流经你的整个应用,都能轻松完成。

Elasticsearch 集成

17、Spring Data 框架集成

Spring Data 是一个用于简化数据库、非关系型数据库、索引库访问,并支持云服务的开源框架,其主要目标是使得对数据的访问变得快捷,并支持 map-reduce 框架和云计算数据服务。

注意版本对应关系!!! springboot、spring-boot-starter-data-elasticsearch、es 版本要对应

1
2
3
4
5
6
7
8
9
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

18、Spark Streaming 框架集成

Spark Streaming 是 Spark core API 的扩展,支持实时数据流的处理,并且具有可扩展,高吞吐量,容错的特点。数据可以从许多来源获取,如 Kafka、Flume、Kinesis 或 TCP sockets,并且可以使用复杂的算法进行处理,这些算法的使用诸如 map、reduce、join 和 window 等高级函数表示。最后,处理后的数据可以推送到文件系统、数据库等。实际上可以将 Spark 的机器学习和图形处理算法应用于数据流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.16.3</version>
</dependency>

<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.16.3</version>
</dependency>

<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.12</artifactId>
<version>3.1.2</version>
</dependency>

<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.12</artifactId>
<version>3.1.2</version>
</dependency>

Apache Spark 是一种基于内存的快速、通用、可扩展的大数据分析计算引擎。

Elasticsearch 优化

20、硬件

Elasticsearch 的基础是 Lucene,所有的索引和文档数据是存储在本地的磁盘中,具体的路径可在 ES 的配置文件 ../config/elasticsearch

磁盘能处理的吞吐量越大,节点越稳定

  • 使用 SSD
  • 使用 RAID 0
  • 使用多块硬盘,并允许 Elasticsearch 通过多个 path.data 目录配置把数据条带化分配到它们上面
  • 不要使用远程挂载的存储,比如 NFS 或者 SMB/CIFS

21、分片策略

21.1、合理设置分片数

分片和副本的设计为 ES 提供了支持分布式和故障转移的特性,但并不意味着分片和副本是可以无限分配的。而且索引的分片完成分配后由于索引的路由机制,是不能重新修改分片数的。

原则:

  • 控制每个分片占用的硬盘容量不超过 ES 的最大 JVM 的堆空间设置(一般设置不超过 32G),因此索引的总容量在 500G 左右,那分片大小在 16 个即可。

  • 考虑一下 node 的数量,一般一个节点有时候就是一台物理机,如果分片数过多,大大超过了节点数,很可能会导致一个节点上存在多个分片,一旦该节点故障,即使保持了 1 个以上的副本,同样有可能会导致数据丢失,集群无法恢复。所以,一般都设置分片数不超过节点数的 3 倍。

  • 主分片,副本和节点最大数之间数量

    1
    节点数<=主分片数 * (副本数 + 1)

21.2、推迟分片分配

对于节点瞬时中断的问题,默认情况,集群会等待一分钟来查看节点是否会重新加入,如果这个节点在此期间重新加入,重新加入的节点会保持其现有的分片数据,不会触发新的分片分配。这样就减少了 ES 在自动再平衡可用分片时所带来的极大开销。

通过修改参数 delayed_timeout,可以延长再均衡时间,可以全局设置也可以在索引级别进行修改:

1
2
3
4
5
6
PUT /_all/_settings
{
"settings":{
"index.unassigned.node_left.delayed_timeout": "5m"
}
}

22、路由选择

当查询文档时,Elasticsearch 通过公式计算访问哪一个分片:

1
shard = hash(routing) % number_of_primary_shards

routing 默认值是文档的 id,也可以采用自定义值,比如用户 id

22.1、不带 routing 查询

在查询的时候因为不知道要查询的数据具体在哪个分片上,所有整个步骤分为 2 个步骤:

  • 分发:请求到达协调节点后,协调节点将查询请求分发到每个分片上
  • 聚合:协调节点搜集到每个分片上的查询结果,在将查询结果进行排序后,给用户返回结果

22.2、带 routing 查询

查询的时候,可以直接根据 routing 信息定位到某个分配查询,不需要查询所有分配,经过协调节点排序

23、写入速度优化

对于搜索性能要求不高,但是对写入要求较高的场景,需要尽可能的选择恰当写优化策略

  • 加大 Translog Flush,目的是降低 Iops、Writeblock
  • 增加 Index Refresh 间隔,目的是减少 Segment Merge 的次数
  • 调整 Bulk 线程池和队列
  • 优化节点间的任务分布
  • 优化 Lucene 层的索引建立,目的是降低 CPU 及 IO

23.1、批量数据提交

ES 提供了 Bulk API 支持批量操作,当我们有大量的写任务的时候,可以使用 Bulk 来进行批量写入

23.2、优化存储设备

ES 是一种密集使用磁盘的应用,在段合并的时候会频繁操作磁盘,所以对磁盘要求较高,当磁盘速度提升后,集群的整体性能会大幅提高

23.3、合理使用合并

Lucene 以段的形式存储数据,当有新的数据写入索引时,Lucene 就会自动创建一个新的段

随着数据量的变化,段的数量会越来越多,消耗的多文件句柄数及 CPU 就越多,查询效率就会下降

23.4、减少 refresh 的次数

Lucene 在新增数据时,采用了延迟写入的策略,默认情况下索引的 refresh_interval 为 1 秒

Lucene 将待写入的数据先写到内存中,超过 1 秒(默认)时就会触发一次 Refresh,然后 refresh 会把内存中的数据刷新到操作系统的文件缓存系统中

如果对搜索的时效性要求不高,可以将 refresh 周期延长,例如 30 秒

可以有效减少段刷新次数,但这同时意味着消耗更多的 Heap 内存

23.5、加大 Flush 设置

Flush 的主要目的是把文件缓存系统中的段持久化到硬盘,当 Translog 的数量达到 512MB 或者 30 分钟时,会触发一次 Flush。

index.translog.flush_threshold_size 参数的默认值是 512MB

增加参数值意味着文件缓存系统中可能需要存储更多的数据,所以需要为操作系统的文件缓存系统留下足够的空间

23.6、减少副本的数量

ES 为了保证集群的可用性,提供了 Replicas(副本)支持,然而每个副本也会执行分析、索引及可能的合并过程,所以 Replicas 的数量会严重影响写索引的效率。

当写索引时,需要把写入的数据都同步到副本节点,副本节点越多,写索引的效率就越慢

如果需要进行大批量的写入操作,可以先禁止 Replica 复制,设置 index.number_of_replicas:0 关闭副本,在写入完成之后,Replica 修改回正常的状态

内存设置

默认为 1 g,ES 堆内存的分配原则

  • 不要超过物理内存的 50 %:Lucene 的设计目的是把底层 OS 的数据缓存到内存中。
    Lucene 的段是分别存储到单个文件中的,这些文件是不会变化的,所以很利于缓存,同时操作系统也会把这些段文件缓存起来,以便更快的访问
  • 堆内存的大小最好不要超过 32 G:在 java 中,所有对象都分配在堆上,然后有一个 Klass pointer 指针指向它的类元数据
  • Copyrights © 2022-2023 hqz

请我喝杯咖啡吧~

支付宝
微信