es基本知识

Elasticsearch能做什么基本概念

  • 为APP或网站增加搜索功能
  • 存储和分析日志、指标和安全事件数据
  • 使用机器学习实时自动建模数据的行为
  • 使用Elasticsearch作为存储引擎自动化业务工作流
  • 使用Elasticsearch作为地理信息系统(GIS)管理、集成和分析空间信息
  • 使用Elasticsearch作为生物信息学研究工具存储和处理遗传数据

Elasticsearch 架构与工作原理

架构概述

Elasticsearch 架构主要由三个组件构成:索引、分片和节点。

  • 索引是文档的逻辑分组,类似于数据库中的表;
  • 分片是索引的物理分区,用于提高数据分布和查询性能;
  • 节点是运行 Elasticsearch 的服务器实例。

工作原理

Elasticsearch 通过以下步骤完成搜索和分析任务:

  1. 接收用户查询请求:Elasticsearch 通过 RESTful API 或 JSON 请求接收用户的查询请求。
  2. 路由请求:接收到查询请求后,Elasticsearch 根据请求中的索引和分片信息将请求路由到相应的节点。
  3. 执行查询:节点执行查询请求,并在相应的索引中查找匹配的文档。
  4. 返回结果:查询结果以 JSON 格式返回给用户,包括匹配的文档和相关字段信息。

Elasticsearch基本概念

索引(Index)

  • 可以理解为数据库中的表

  • 索引是存储相关数据的数据结构。每个索引都有自己的映射(mapping),用于定义每个字段的数据类型和其他属性。

  • 在创建索引时,我们需要指定一些参数,比如分片数量和副本数量。分片是将索引数据水平切分为多个小块的过程,这样可以提高数据检索和处理的效率。副本则是将索引数据复制到一个或多个节点上,以提高数据的可靠性和查询的可用性。

  • 索引的映射(mapping)是用于定义索引中每个字段的数据类型和其他属性。在创建索引时,需要定义每个字段的数据类型(如文本、数字、日期等)和其他属性(如是否需要分析、是否存储等)。此外,映射还可以定义其他高级功能,如聚合、排序和过滤等。

类型(Type)

从Elasticsearch 7.x版本开始,索引中的每个文档都直接属于一个索引,而不再需要指定类型。这主要是为了简化索引和查询操作,提高查询效率。因此,在新的版本中,类型这个概念已经逐渐被淘汰。

文档(Document)

  • 可以理解为数据库中的行

  • 文档是Elasticsearch中存储和检索的基本单位,它是序列化为JSON格式的数据结构。每个文档都有一个唯一的标识符,称为_id字段,用于唯一标识该文档。每个文档都存储在一个索引中,并且可以包含多个字段,这些字段可以是不同的数据类型,如文本、数字、日期等。

  • 在Elasticsearch中,文档的属性包括_index_type_source等。_index表示文档所属的索引名称,_type表示文档所属的类型名称(在早期的Elasticsearch版本中,这是必需的,但在7.x版本之后已经不再需要),_source表示文档的原始JSON数据。

Elasticsearch集群基本概念

集群(Cluster)

  • 一个Elasticsearch集群通常包含了多个节点(Node)和一个或多个索引(Index),并且这些节点和索引共同构成了整个Elasticsearch集群,在所有节点上提供联合索引和搜索功能。

  • 每个Cluster都有一个唯一的名称,即cluster name,它用于标识和区分不同的Elasticsearch集群。

节点(Node)

  • 在Elasticsearch集群中,Node是指运行Elasticsearch实例的服务器。每个Node都有自己的名称和标识符,并且都有自己的数据存储和索引存储。

  • 一个Elasticsearch集群由一个或多个Node组成,这些Node通过它们的集群名称进行标识。在默认情况下,如果Elasticsearch已经开始运行,它会自动生成一个叫做“elasticsearch”的集群。我们也可以在配置文件(elasticsearch.yml)中定制我们的集群名字。

  • Node在Elasticsearch中扮演着不同的角色。根据节点的配置和功能,可以将Node分为以下几种类型:

    • Master Node:负责整个Cluster的配置和管理任务,如创建、更新和删除索引,添加或删除Node等。一个Cluster中至少需要有一个Master Node。
    • Data Node:主要负责数据的存储和处理,它们可以处理数据的CRUD操作、搜索操作、聚合操作等。一个Cluster中可以有多个Data Node。
    • Ingest Node:主要负责对文档进行预处理,如解析、转换、过滤等操作,然后再将文档写入到Index中。每个Cluster中至少需要有一个Ingest Node。 除了上述的三种类型外,还可以有Tribe Node、Remote Cluster Client等特殊用途的Node。
  • Node之间是对等关系(去中心化),每个节点上面的集群状态数据都是实时同步的。如果Master节点出故障,按照预定的程序,其他一台Node机器会被选举成为新的Master。

  • 需要注意的是,一个Node可以同时拥有一种或几种功能,如一个Node可以同时是Master Node和Data Node。

分片(Shards)

每个分片放到不同的服务器上。每个分片可以有零个或多个副本。这不仅能够提高查询效率,还能够提高系统的可靠性和可用性。如果某个节点或Shard发生故障,Elasticsearch可以从其他节点或Shard的副本中恢复数据,从而保证数据的可靠性和可用性。

对于每个索引,在创建时需要指定主分片的数量,一旦索引创建后,主分片的数量就不能更改。

副本(Replicas)

  • 提高系统的容错性。当某个节点发生故障,或者某个分片(Shard)损坏或丢失时,可以从副本中恢复数据。这意味着,即使一个节点或分片出现问题,也不会导致整个索引的数据丢失。这种机制可以增加系统的可靠性,并减少因节点或分片故障导致的宕机时间。
  • 提高查询效率。Elasticsearch会自动对搜索请求进行负载均衡,可以将搜索请求分配到多个节点上,从而并行处理搜索请求,提高查询效率。这种负载均衡机制可以在节点之间分发查询请求,使得每个节点都可以处理一部分查询请求,从而避免了一个节点的瓶颈效应。

副本可以在创建索引时或之后增加,提供了一种灵活的方式来根据需要调整系统的容错能力和读取性能。

搭建环境

(1)下载分词器

1
2
3
4
5
6
7
mkdir /docker

cd /docker

wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v8.9.2/elasticsearch-analysis-ik-8.9.2.zip

unzip elasticsearch-analysis-ik-8.9.2.zip -d elasticsearch-analysis-ik

(2)DockerFile

1
2
3
FROM docker.elastic.co/elasticsearch/elasticsearch:8.9.2
# 安装 IK 分词插件
ADD elasticsearch-analysis-ik /usr/share/elasticsearch/plugins/elasticsearch-analysis-ik

执行:docker build -f DockerFile -t elasticsearch-ik:8.9.2 .

(3)docker-compose.yml

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
version: "3.7"

services:
elasticsearch:
container_name: elasticsearch-ik
image: elasticsearch-ik:8.9.2
environment:
- node.name=elasticsearch
- ES_JAVA_OPTS=-Xms512m -Xmx512m
- discovery.type=single-node
- xpack.security.enabled=false
ports:
- 9200:9200
- 9300:9300
networks:
- elastic
kibana:
image: docker.elastic.co/kibana/kibana:8.9.2
container_name: kibana
ports:
- 5601:5601
networks:
- elastic
depends_on:
- elasticsearch

networks:
elastic:

执行 docker-compose up

待容器启动后,在本机浏览器打开 http://127.0.0.1:5601即可看到如下kibana管理界面。

常用命令

基本信息

查看基本信息

1
curl localhost:9200
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"name" : "elasticsearch", #es当前机器的名字
"cluster_name" : "docker-cluster", #节点所属集群的名称
"cluster_uuid" : "9c09PuzgQSGcUMEGvv43Rw", #集群给当前机器唯一的id
"version" : {
"number" : "8.9.2",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "e8179018838f55b8820685f92e245abef3bddc0f",
"build_date" : "2023-08-31T02:43:14.210479707Z",
"build_snapshot" : false,
"lucene_version" : "9.7.0",
"minimum_wire_compatibility_version" : "7.17.0",
"minimum_index_compatibility_version" : "7.0.0"
},
"tagline" : "You Know, for Search"
}

查看健康状态

1
GET /_cat/health?v

查看节点数目

1
GET /_cat/nodes

查看所有索引

1
GET /_cat/indices?v

索引操作

创建索引

例如,下面的命令是在 Elasticsearch 集群创建一个名为{index}的新索引。

1
PUT /{index}
1
2
3
4
5
{
"acknowledged": true, //成功
"shards_acknowledged": true, //索引的分片已经被集群成功识别并准备好了
"index": "blog_posts" //index_name
}

查询索引

1
GET /{index}
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
{
"{index}": { //索引名
"aliases": {}, //别名
"mappings": {}, //映射
"settings": { //当前索引设置项
"index": { //针对于索引的设置
"routing": { //路由
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards": "1", //分片数量
"provided_name": "{index}", //索引名
"creation_date": "1712842040451",
"number_of_replicas": "1",
"uuid": "FWVtpGPUTUKxSM9t9xYIdw", //索引在底层的唯一标识
"version": {
"created": "8090299"
}
}
}
}
}

修改索引

不允许修改分片数目

1
2
3
4
5
6
7
PUT /{index}/_settings

{
"index": {
"number_of_shards": "3"
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"error": {
"root_cause": [
{
"type": "illegal_argument_exception",
"reason": "Can't update non dynamic settings [[index.number_of_shards]] for open indices [[{index}/FWVtpGPUTUKxSM9t9xYIdw]]"
}
],
"type": "illegal_argument_exception",
"reason": "Can't update non dynamic settings [[index.number_of_shards]] for open indices [[{index}/FWVtpGPUTUKxSM9t9xYIdw]]"
},
"status": 400
}

删除索引

1
DELETE /{index}

文档操作

创建

创建文档(随机id)

注意,这个id并不是文档的id

1
2
3
4
5
6
7
8
POST /{index}/_doc

{
"id":"1",
"title": "这是一个超级牛逼的帖子",
"content": "今天去玩了,真的好开心呀",
"publish_date": "2024-04-11"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"_index": "{index}", //保存到的索引
"_id": "joNnzY4BUfC06Ch4uPc-", //刚才数据的唯一id
"_version": 1, //数据的版本号
"result": "created", //结果
"_shards": { //分片工作
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0, //乐观锁机制
"_primary_term": 1 //乐观锁机制
}

这个操作不幂等

创建(修改)文档(指定id)

这个操作是幂等的,变的只有_seq_no

这个操作会直接覆盖掉之前文档的设置,慎用!!!

1
2
3
4
5
6
POST /{index}/_doc/{id}
{
"title": "这是一个超级牛逼的帖子",
"content": "今天去玩了,真的好开心呀",
"publish_date": "2024-04-11"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"_index": "{index}",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 43,
"_primary_term": 1
}

查询

查询所有文档

1
GET /{index}/_search
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
{
"took": 1, //耗时1ms
"timed_out": false,
"_shards": { //查询涉及的分片的信息
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": { //命中的记录
"total": { //统计的数量
"value": 3, //有三条记录
"relation": "eq"
},
"max_score": 1.0, //最大得分
"hits": [ //真正查出来的数据
{
"_index": "{index}",
"_id": "n4NvzY4BUfC06Ch4xvem",
"_score": 1.0,
"_source": { //内容
"id": "1",
"title": "这是一个超级牛逼的帖子",
"content": "今天去玩了,真的好开心呀",
"publish_date": "2024-04-11"
}
},
{
"_index": "{index}",
"_id": "oINvzY4BUfC06Ch4zfdn",
"_score": 1.0,
"_source": {
"id": "1",
"title": "这是一个超级牛逼的帖子",
"content": "今天去玩了,真的好开心呀",
"publish_date": "2024-04-11"
}
},
{
"_index": "{index}",
"_id": "oYNvzY4BUfC06Ch40Pcn",
"_score": 1.0,
"_source": {
"id": "1",
"title": "这是一个超级牛逼的帖子",
"content": "今天去玩了,真的好开心呀",
"publish_date": "2024-04-11"
}
}
]
}
}

查询特定文档

1
GET /{index}/_doc/{id}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"_index": "{index}",
"_id": "oYNvzY4BUfC06Ch40Pcn",
"_version": 1,
"_seq_no": 42,
"_primary_term": 1,
"found": true,
"_source": {
"id": "1",
"title": "这是一个超级牛逼的帖子",
"content": "今天去玩了,真的好开心呀",
"publish_date": "2024-04-11"
}
}

查询文档source

1
GET /{index}/_source/1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"id": 1,
"userID": 147982601,
"score": 5,
"status": 2,
"publishTime": "2023-09-09T16:07:42.499144+08:00",
"content": "这是一个好评!",
"tags": [
{
"code": 1000,
"title": "好评"
},
{
"code": 2000,
"title": "物超所值"
},
{
"code": 3000,
"title": "有图"
}
]
}

获取指定字段

1
GET /{index}/_source/{id}?_source=content,score
1
2
3
4
{
"score": 5,
"content": "这是一个好评!"
}

查询所有文档

1
2
3
4
5
6
POST /{index}/_search
{
"query": {
"match_all": {}
}
}

模糊查询—match

match 查询用于执行全文搜索,主要用于全文字段(如 text 类型字段)。它会先对查询字符串进行分析,然后再进行搜索。

适用于全文搜索场景,如搜索文章内容、评论等。

1
2
3
4
5
6
7
8
9
POST /{index}/_search

{
"query": {
"match": {
"{field_name}": "{value}"
}
}
}

短句匹配—match_phrase

1
2
3
4
5
6
7
8
9
POST /{index}/_search

{
"query": {
"match_phrase": {
"{field_name}": "{query_phrase}"
}
}
}
  • {index}:目标索引名称。
  • {field_name}:目标字段名称。
  • {query_phrase}:你希望按照精确顺序匹配的短语。

精确查询—term

term 查询用于执行精确匹配搜索,主要用于非全文字段(如 keyword 类型字段)或者完全匹配文本字段的精确值。

适合用于标签、ID、状态码等需要精确匹配的场合。

1
2
3
4
5
6
7
8
9
10
11
POST /{index}/_search

{
"query": {
"term": {
"{field_name}.keyword": {
"value": "{value}"
}
}
}
}

多个字段查询

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /{index}/_search

{
"query": {
"multi_match": {
"query": "{value}",
"fields": [
"{field_name1}",
"{field_name2}"
]
}
}
}

terms查询

一个字段匹配多个值中的任何一个

1
2
3
4
5
6
7
8
9
10
11
12
POST /{index}/_search

{
"query": {
"terms": {
"{field_name}": [
"{value1}",
"{value2}"
]
}
}
}

查询特定字段

1
2
3
4
5
6
7
8
POST /{index}/_search

{
"query": {
"match_all": {}
},
"_source":["content"]
}

返回结果只包含content

区间查询

查询age在[20,25]的

1
2
3
4
5
6
7
8
9
10
11
12
POST /{index}/_search

{
"query": {
"range": {
"{field_name}": {
"gte": min_value,
"lte": max_value
}
}
}
}

复合查询(must,should)

需要计算评分,must强制符合,should不强制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
POST /{index}/_search

{
"query": {
"bool": {
"must": [
{
"match": {
"{field_name}": "{value}"
}
},
{
"range": {
"{range_field_name}": {
"gte": min_value,
"lte": max_value
}
}
}
]
}
}
}

过滤(filter)

基本和must相同,就是不计算得分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
POST /{index}/_search

{
"query": {
"bool": {
"filter": [
{
"match": {
"{field_name}": "{value}"
}
},
{
"range": {
"{range_field_name}": {
"gte": min_value,
"lte": max_value
}
}
}
]
}
}
}
  • 不计算得分filter 不会为满足条件的文档计算相关性得分,因此如果你不关心返回文档的顺序,只是想要快速筛选出符合条件的文档,这会更有效率。
  • 缓存filter 中的条件结果会被 Elasticsearch 缓存,对于重复的、相似的查询,这可以显著提高性能。

分页查询

1
2
3
4
5
POST /{index}/_search
{
"from": 0, // 从第一个结果开始
"size": 10 // 返回10个结果
}

排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
POST /{index}/_search
{
"from": 0, // 从第一个结果开始
"size": 10, // 返回10个结果
"sort": [
{
"{sort_field_name}": {
"order": "asc" // "asc"为升序,"desc"为降序
}
},
{
"_score": {
"order": "desc" // 根据相关性得分降序排序
}
}
]
}

修改

修改文档字段

1
2
3
4
5
6
POST /{index}/_update/{id}
{
"doc": {
"name": "更新后的名字"
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"_index": "review-1",
"_id": "1",
"_version": 11,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 53,
"_primary_term": 1
}

删除

删除文档

1
DELETE /{index}/_doc/1
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"_index": "{index}",
"_id": "1",
"_version": 3,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1
}

批量操作

批量操作可以减少网络请求,增加效率,非事务

获取数量

例如,查询content中包含差评的文档数量。

1
2
3
4
5
6
7
8
GET /{index}/_count
{
"query": {
"match_phrase": {
"content": "差评"
}
}
}
1
2
3
4
5
6
7
8
9
{
"count": 0,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
}
}

聚合

1
2
3
4
5
6
7
8
9
10
11
12
POST /{index}/_search
{
"size": 0, // 通常设置为0,因为我们不需要获取具体的文档,只关注聚合结果
"aggs": {
"average_value": { // 自定义的聚合名称
"avg": {
"field": "{field_name}" // 替换为你需要计算平均值的字段
}
}
}
}

子聚合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
POST /{index}/_search
{
"size": 0,
"aggs": {
"group_by_field": { // 按字段分组
"terms": {
"field": "{group_field_name}" // 分组字段
},
"aggs": {
"average_value": { // 每个分组内计算平均值
"avg": {
"field": "{value_field_name}" // 需要计算平均值的字段
}
}
}
}
}
}

高亮

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /{index}/_search
{
"query": {
"match": {
"{field_name}": "{query_text}"
}
},
"highlight": {
"fields": {
"{field_name}": {}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
POST /{index}/_search
{
"query": {
"match": {
"{field_name}": "{query_text}"
}
},
"highlight": {
"fields": {
"{field_name}": {
"pre_tags": ["<strong>"],
"post_tags": ["</strong>"]
}
},
"order": "score", // 根据匹配得分排序高亮片段
"number_of_fragments": 3, // 每个字段返回的高亮片段数量
"fragment_size": 150 // 每个高亮片段的字符长度
}
}

乐观锁

在 Elasticsearch 中,乐观并发控制(Optimistic Concurrency Control, OCC)可以通过使用版本号(_version)或序列号(_seq_no)和主要术语(_primary_term)来实现。这些机制确保了在更新文档时,只有在文档没有被其他操作更改的情况下,更新才会执行。下面是如何在 URL 上体现乐观锁机制的两个示例:

使用版本号(_version)

当你通过版本控制更新文档时,可以在查询参数中指定 _version。例如,如果你知道文档的当前版本,并希望更新只在这个版本基础上进行,可以这样做:

1
2
3
4
5
6
POST /{index}/_update/{id}?version=2
{
"doc": {
"content": "更新后的内容"
}
}

这个请求尝试更新索引 posts_id1 的文档,并且这个更新只有在文档的当前版本为 2 时才会执行。如果当前版本不是 2(例如,文档已被其他操作更新),则更新会失败。

使用序列号(_seq_no)和主要术语(_primary_term)

对于更细粒度的并发控制,可以使用 _seq_no_primary_term。这两个参数更精确地控制并发更新,确保只有在你上次读取文档后没有任何更改时,更新才会进行:

1
2
3
4
5
6
POST /{index}/_update/{id}?if_seq_no=10&if_primary_term=1
{
"doc": {
"content": "更新后的内容"
}
}

这个请求尝试更新索引 posts_id1 的文档,但只在序列号为 10 且主要术语为 1 时才执行。这意味着你的更新操作是基于你最后一次已知文档状态的。如果文档在你读取之后已经被更新(即序列号或主要术语改变),则更新操作会失败。

注意事项

  • 使用乐观锁机制时,需要先查询文档以获取其当前的 _version_seq_no_primary_term 值。
  • 如果更新失败(因为文档已被更改),应用程序逻辑需要决定如何处理这种冲突,比如重试操作或者通知用户。
  • 乐观并发控制机制特别适合在文档更新频率较低或冲突可能性较小的场景中使用,因为它可以避免锁的开销,同时确保数据的一致性。

映射与分词

Mappings

  • ES里面保存是json
  • 第一保存了以后每个索引的数据模型就确定好了,es在第一次保存一个ison文档的时候就自动确定了。·
  • 在自动确定阶段,所有文本数据都会被确定为text。默认就可以全文检索。这个文本值在存储的时候分词,利用倒排索引存
  • Mappings第一次已经确定好的属性就不能修改
  1. Text类型:
    • 用于全文搜索。当你需要对文本进行操作,如搜索文中的词汇时,使用text类型。
    • text字段在索引时会被分析器分解成一系列的单词(或称为tokens),这允许Elasticsearch执行全文搜索。
    • 由于分词的原因,text字段可以用来进行模糊查询、近义词查询等。
  2. Keyword类型:
    • 适用于精确值匹配,如日志的标签、身份标识符、枚举值等。
    • keyword字段不会进行分词处理,字段中的内容会作为一个整体进行索引。
    • 这种类型非常适合进行过滤、排序、聚合操作。例如,如果你需要按照某个字段进行分组或统计信息,那么这个字段应该是keyword类型。

分词

(1)分词

分词是将文本分解成更小的可搜索单元(通常是单词)的过程。这在text类型字段中非常关键,因为它允许进行复杂的全文搜索。

  • 英文文本:通常以空格和标点符号作为分词界限。
  • 中文文本:由于中文写作不使用空格分隔单词,所以分词更复杂,需要依赖词库和算法来识别单词边界。

(2)存储(Storage)

在分词后,Elasticsearch会利用倒排索引技术来存储文本数据:

  • 倒排索引:这是一种索引结构,它存储了每个单词和这个单词出现在哪些文档中的映射。当你查询一个特定的词时,Elasticsearch可以快速地找到包含该词的所有文档。

(3)检索(Retrieval)

检索是搜索过程中的最后一步,通过倒排索引可以高效地执行:

  • 全文搜索查询:当用户提交查询时,Elasticsearch会对查询文本进行相同的分词过程,然后使用倒排索引找到包含这些词的文档。
  • 相关性打分:Elasticsearch不仅找到包含查询词的文档,还会根据多种因素(如词频、文档频率等)计算每个文档的相关性得分,以此来排序搜索结果。

利用ik分词器处理中文

自定义词库

nested嵌入式索引

为什么引入呢?

扁平化(flattening)是指将嵌套的数据结构(如对象数组)转换为平面结构的过程。这通常发生在不使用nested类型时,而是将复杂对象的字段作为同级字段处理。扁平化可以影响查询的精度和相关性,因为它破坏了原始数据中字段之间的逻辑关联。

未扁平化的原始文档结构:

1
2
3
4
5
6
{
"employees": [
{ "name": "Alice", "department": "Marketing" },
{ "name": "Bob", "department": "Sales" }
]
}

扁平化后的结构:

1
2
3
4
{
"employees.name": ["Alice", "Bob"],
"employees.department": ["Marketing", "Sales"]
}

在这种扁平化结构中,employees.nameemployees.department 作为独立的数组存储。这意味着他们之间的关系在物理存储上被分离了。

解决方案

定义嵌套字段

在创建索引时,你需要指定某个字段为nested类型。这告诉Elasticsearch这个字段包含的是多个独立的对象,每个对象应该被单独索引和查询。

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
PUT /my_index
{
"mappings": {
"properties": {
"user": {
"type": "nested", // 声明nested类型
"properties": {
"first_name": {
"type": "text"
},
"last_name": {
"type": "text"
},
"address": {
"type": "nested", // 嵌套字段可以进一步嵌套
"properties": {
"street": {
"type": "text"
},
"city": {
"type": "text"
},
"state": {
"type": "keyword"
}
}
}
}
}
}
}
}

查询嵌套字段

当你需要查询嵌套字段时,必须使用nested查询。这样Elasticsearch才能正确地处理嵌套对象内部的字段关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /my_index/_search
{
"query": {
"nested": {
"path": "user.address",
"query": {
"bool": {
"must": [
{ "match": { "user.address.city": "San Francisco" } },
{ "match": { "user.address.state": "CA" } }
]
}
}
}
}
}

重建索引

重建索引是一个常见的任务,尤其是在需要更改索引映射、优化索引性能或者迁移数据到新版本的Elasticsearch时。

1.创建一个新的索引

2.将现有索引中的数据复制到新索引中。

创建新的索引和映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PUT /new_index
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"field1": {
"type": "text"
},
"field2": {
"type": "keyword"
}
}
}
}

使用_reindex API

1
2
3
4
5
6
7
8
9
POST /_reindex
{
"source": {
"index": "old_index"
},
"dest": {
"index": "new_index"
}
}

分片和副本

读写流程

集群部署

haproxy.cfg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
global
log stdout format raw local0
maxconn 4096

defaults
log global
mode http
option httplog
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms

frontend http_front
bind *:80
default_backend es_back

backend es_back
balance roundrobin
option httpchk GET /
server elasticsearch1 elasticsearch1:9200 check
server elasticsearch2 elasticsearch2:9200 check
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
version: '3.8'

services:
haproxy: #端口转发
image: haproxy:latest
container_name: haproxy
ports:
- "80:80"
volumes:
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
networks:
- elastic

elasticsearch1:
image: elasticsearch:7.8.0
container_name: es7_01
environment:
- node.name=es7_01
- cluster.name=es-cluster
- discovery.seed_hosts=elasticsearch1,elasticsearch2
- cluster.initial_master_nodes=es7_01,es7_02
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- esdata1:/usr/share/elasticsearch/data
networks:
- elastic

elasticsearch2:
image: elasticsearch:7.8.0
container_name: es7_02
environment:
- node.name=es7_02
- cluster.name=es-cluster
- discovery.seed_hosts=elasticsearch1,elasticsearch2
- cluster.initial_master_nodes=es7_01,es7_02
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- esdata2:/usr/share/elasticsearch/data
networks:
- elastic

volumes:
esdata1:
driver: local
esdata2:
driver: local

networks:
elastic:
driver: bridge

es基本知识
http://example.com/2024/04/11/elasticsearch/基础概念/
作者
Mrxiad
发布于
2024年4月11日
许可协议