ElasticSearch 详细介绍

ElasticSearch 笔记

简介

The Elastic Stack, 包括 Elasticsearch、Kibana、Beats 和 Logstash(也称为 ELK Stack)。能够安全可靠地获取任何来源、任何格式的数据,然后实时地对数据进行搜索、分析和可视化。Elaticsearch,简称为 ES,ES 是一个**开源的高扩展的分布式全文搜索引擎,**是整个 Elastic Stack 技术栈的核心。它可以近乎实时存储检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB 级别的数据。

意义

提高对结构化数据和非结构化数据的查询效率

全文搜索引擎

Google,百度类的网站搜索,它们都是根据网页中的关键字生成索引,我们在搜索的时候输入关键字,它们会将该关键字即索引匹配到的所有网页返回;还有常见的项目中应用日志的搜索等等。对于这些非结构化的数据文本,关系型数据库搜索不是能很好的支持。

一般传统数据库,全文检索都实现的很鸡肋,因为一般也没人用数据库存文本字段。进行全文检索需要扫描整个表,如果数据量大的话即使对 SQL 的语法优化,也收效甚微。建立了索引,但是维护起来也很麻烦,对于 insert 和 update 操作都会重新构建索引。

为了解决结构化数据搜索和非结构化数据搜索性能问题,我们就需要专业,健壮,强大的全文搜索引擎

这里说到的全文搜索引擎指的是目前广泛应用的主流搜索引擎。它的工作原理是计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。这个过程类似于通过字典中的检索字表查字的过程。

相关知识

JSON字符串:网络中传递的字符串的格式符合JSON格式

Postman

如果直接通过浏览器向Elasticsearch 服务器发请求,那么需要在发送的请求中包含HTTP 标准的方法,而 HTTP 的大部分特性且仅支持 GET 和POST 方法。所以为了能方便地进行客户端的访问,可以使用Postman 软件

数据格式

Elasticsearch 是面向文档型数据库,一条数据在这里就是一个文档。为了方便大家理解, 我们将 Elasticsearch 里存储文档数据和关系型数据库 MySQL 存储数据的概念进行一个类比

ES 里的 Index 可以看做一个,而Types 相当于Documents 则相当于表的

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

用JSON 作为文档序列化的格式,比如一条用户信息:

{
"name" : "John",
"sex" : "Male", 
"age" : 25,
"birthDate": "1990/05/01",
"about" : "I love to go rock climbing", 
"interests": [ "sports", "music" ]
}

基础操作

创建索引

在 Postman 中,向ES 服务器发 PUT 请求 :http://127.0.0.1:9200/shopping

查看所有索引

在Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/_cat/indices?v

这里请求路径中的_cat 表示查看的意思,indices 表示索引,所以整体含义就是查看当前 ES

服务器中的所有索引,就好像 MySQL 中的 show tables 的感觉

查看单个索引

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/shopping

请求后,服务器响应结果如下:

删除索引

在 Postman 中,向ES 服务器发 DELETE 请求 :http://127.0.0.1:9200/shopping

映射操作

有了索引库,等于有了数据库中的 database。

接下来就需要建索引库(index)中的映射了,类似于数据库(database)中的表结构(table)。创建数据库表需要设置字段名称,类型,长度,约束等;索引库也一样,需要知道这个类型下有哪些字段,每个字段有哪些约束信息,这就叫做映射(mapping)。

创建映射

在 Postman 中,向ES 服务器发 PUT 请求 :http://127.0.0.1:9200/student**/_mapping**

请求体内容为:

{
	"properties": { 
		"name":{
		"type": "text", 
		"index": true
		},
		"sex":{
		"type": "text", 
		"index": false
		},
		"age":{
		"type": "long", 
		"index": false
		}
	}
}

映射数据说明:

字段名:任意填写,下面指定许多属性,例如:name、sex、images、price

type:类型,Elasticsearch 中支持的数据类型非常丰富,说几个关键的:

  • String 类型,又分两种:
    • text:可分词
    • keyword:不可分词,数据会作为完整字段进行匹配
  • Numerical:数值类型,分两类
    • 基本数据类型:long、integer、short、byte、double、float、half_float
    • 浮点数的高精度类型:scaled_float
  • Date:日期类型
  • Array:数组类型
  • Object:对象

index:是否索引,默认为true,也就是说你不进行任何配置,所有字段都会被索引。

​ true:字段会被索引,则可以用来进行搜索

​ false:字段不会被索引,不能用来搜索

store:是否将数据进行独立存储,默认为 false

​ 原始的文本会存储在_source 里面,默认情况下其他提取出来的字段都不是独立存储的,是从_source 里面提取出来的。当然你也可以独立的存储某个字段, 只要设置"store": true 即可,获取独立存储的字段要比从_source 中解析快得多,但是也会占用更多的空间,所以要根据实际业务需求来设置。

analyzer:分词器,这里的ik_max_word 即使用 ik 分词器,后面会有专门的章节学习

查看映射

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student**/_mapping**

索引映射关联

在 Postman 中,向ES 服务器发 PUT 请求 :http://127.0.0.1:9200/student1

{
"settings": {},
"mappings": {
	"properties": {
	"name":{
	"type": "text",
	"index": true
	},
	"sex":{
	"type": "text",
	"index": false
	},
	"age":{
	"type": "long",
	"index": false
	}
}
}
}

创建文档

在 Postman 中,向ES 服务器发 POST 请求 :http://127.0.0.1:9200/shopping**/_doc**

此处发送请求的方式必须为 POST,不能是 PUT,否则会发生错误 ----> put是幂等性的,post不是

上面的数据创建后,由于没有指定数据唯一性标识(ID),默认情况下,ES 服务器会随机生成一个。

如果想要自定义唯一性标识,需要在创建时指定:http://127.0.0.1:9200/shopping/_doc/1

此处需要注意:如果增加数据时明确数据主键,那么请求方式也可以为PUT

查看文档(根据主键查询)

查看文档时,需要指明文档的唯一性标识,类似于MySQL 中数据的主键查询

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/shopping**/_doc/1**

修改文档

全量修改

和新增文档一样,输入相同的URL 地址请求,如果请求体变化,会将原有的数据内容覆盖在 Postman 中

向ES 服务器发 POST 请求 :http://127.0.0.1:9200/shopping**/_doc/1**

请求体内容为:

{
"title":"华为手机",
"category":" 华 为 ", 
"images":"http://www.gulixueyuan.com/hw.jpg", 
"price":4999.00
}

局部修改

在 Postman 中,向ES 服务器发 POST 请求 :http://127.0.0.1:9200/shopping/_update/1

请求体内容为:

{
	"doc": { 
		"price":3000.00
		}
}

删除文档

无条件删除

删除一个文档不会立即从磁盘上移除,它只是被标记成已删除(逻辑删除)。

在 Postman 中,向ES 服务器发 DELETE 请求 :http://127.0.0.1:9200/shopping**/_doc/1**

条件删除

一般删除数据都是根据文档的唯一性标识进行删除,实际操作时,也可以根据条件对多条数据进行删除

向 ES 服务器发 POST 请求 :http://127.0.0.1:9200/shopping**/_delete_by_query**

请求体内容为:

{
	"query":{
		"match":{ 
			"price":4000.00
				}
			}
}

查询

查询所有文档

在Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

在body中加入相关参数

{
"query": {
"match_all": {}
}
}
# "query":这里的 query 代表一个查询对象,里面可以有不同的查询属性
# "match_all":查询类型,例如:match_all(代表查询所有), match,term , range 等等
# {查询条件}:查询条件会根据类型的不同,写法也有差异

匹配查询

match 匹配类型查询,会把查询条件进行分词,然后进行查询,多个词条之间是 or 的关系

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

在body中加入相关参数

{
"query": {
	"match": {
	"name":"zhangsan"
}
}
}
字段匹配查询

multi_match 与 match 类似,不同的是它可以在多个字段中查询。

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
"query": { 
	"multi_match": {
		"query": "zhangsan", 
		"fields": ["name","nickname"]
}
}
关键字精确查询

term 查询,精确的关键词匹配查询,不对查询条件进行分词。

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
"query": {
	"term": {
		"name": {
			"value": "zhangsan"
			}
		}
	}
}
多关键字精确查询

terms 查询和 term 查询一样,但它允许你指定多值进行匹配。

如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件,类似于 mysql 的 in

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
"query": {
	"terms": {
		"name": ["zhangsan","lisi"]
			}
		}
}

指定查询字段

默认情况下,Elasticsearch 在搜索的结果中,会把文档中保存在_source 的所有字段都返回。_

如果我们只想获取其中的部分字段,我们可以添加_source 的过滤

在Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
	"_source": ["name","nickname"], 
	"query": {
		"terms": {
			"nickname": ["zhangsan"]
				}
			}
}
过滤字段

我们也可以通过:

​ Ø includes:来指定想要显示的字段

​ Ø excludes:来指定不想要显示的字段

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
	"_source": {
		"includes": ["name","nickname"]
		},
	"query": {
		"terms": {
		"nickname": ["zhangsan"]
}
}
}

或者

{
	"_source": {
	"excludes": ["name","nickname"]
},
	"query": {
		"terms": {
					"nickname": ["zhangsan"]
				}
			}
}

组合查询

bool把各种其它查询通过must(必须 and)、must_not(必须不)、should(应该 or)的方式进行组合

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
"query": {
	"bool": {
		"must": [
					{
						"match": {
									"name": "zhangsan"
								}
					}
				],
        "must_not": [
						{
							"match": {
										"age": "40"
									}
                 	   }
					],
		"should": [
					{
					"match": {
								"sex": "男"
							}
					}
				]
			}
		}
}

范围查询

range 查询找出那些落在指定区间内的数字或者时间。range 查询允许以下字符

操作符说明
gt大于>
gte大于等于>=
lt小于<
lte小于等于<=

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "category": "小米"
                    }
                }
            ],
            "filter": {
                "range": {
                    "price": {
                        "gte": 30
                    }
                }
            }
        }
    }
}

模糊查询

返回包含与搜索字词相似的字词的文档。

编辑距离是将一个术语转换为另一个术语所需的一个字符更改的次数。这些更改可以包括:

l 更改字符(box → fox)

l 删除字符(black → lack)

l 插入字符(sic → sick)

l 转置两个相邻字符(act → cat)

为了找到相似的术语,fuzzy 查询会在指定的编辑距离内创建一组搜索词的所有可能的变体或扩展。然后查询返回每个扩展的完全匹配。

通过 fuzziness 修改编辑距离。一般使用默认值 AUTO,根据术语的长度生成编辑距离。

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
"query": {
		"fuzzy": {
					"title": {
								"value": "zhangsan"
							}
				}
}
}

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
"query": {
	"fuzzy": {
		"title": {
					"value": "zhangsan", 
					"fuzziness": 2
				}
			}
		}
}

单字段排序

sort 可以让我们按照不同的字段进行排序,并且通过 order 指定排序的方式。desc 降序,asc 升序。

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
	"query": {
		"match": {
			"name":"zhangsan"
				}
			},
	"sort": [
				{
					"age": {
							"order":"desc"
						 }
				}
			]
}

多字段排序

假定我们想要结合使用 age 和 _score 进行查询,并且匹配的结果首先按照年龄排序,然后按照相关性得分排序

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
	"query": {
		"match_all": {}
	},
	"sort": [
				{
					"age": {
								"order": "desc"
						}
				},
				{
					"_score":{
								"order": "desc"
							}
				}
			]
}

高亮查询

在进行关键字搜索时,搜索出的内容中的关键字会显示不同的颜色,称之为高亮。

Elasticsearch 可以对查询内容中的关键字部分,进行标签和样式(高亮)的设置。在使用 match 查询的同时,加上一个 highlight 属性:

l pre_tags:前置标签

l post_tags:后置标签

l fields:需要高亮的字段

l title:这里声明 title 字段需要高亮,后面可以为这个字段设置特有配置,也可以空

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
	"query": {
				"match": {
							"name": "zhangsan"
						}
			},
	"highlight": {
					"pre_tags": "<font color='red'>", 
					"post_tags": "</font>",
					"fields": {
								"name": {}
							}
				}
}

分页查询

from:当前页的起始索引,默认从 0 开始。 from = (pageNum - 1) * size

size:每页显示多少条

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
	"query": {
				"match_all": {}
			},
	"sort": [
				{
					"age": {
							"order": "desc"
						}
				}
			],
	"from": 0,
	"size": 2
}

聚合查询

聚合允许使用者对 es 文档进行统计分析,类似与关系型数据库中的 group by,当然还有很多其他的聚合,例如取最大值、平均值等等。

对某个字段取最大值 max

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
"aggs":{
		"max_age":
				{ 
					"max":{
							"field":"age"
						}
				}
		},
"size":0
}

对某个字段取最小值 min

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
	"aggs":{
			"min_age":{ 
						"min":{
								"field":"age"
							}
					}
			},
	"size":0
}

对某个字段求和sum

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
	"aggs":{
			"sum_age":
						{ 
						"sum":{
								"field":"age"
							}
						}
		},
	"size":0
}

对某个字段取平均值 avg

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
	"aggs":{
			"avg_age":
				{ 
					"avg":{
							"field":"age"
						}
				}
		},
	"size":0
}

对某个字段的值进行去重之后再取总数

在Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
	"aggs":{ 
		"distinct_age":{
						"cardinality":{
										"field":"age"
									}
					}
		},
	"size":0
}

State 聚合

stats 聚合,对某个字段一次性返回 count,max,min,avg 和 sum 五个指标

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
	"aggs":{
		"stats_age":{ 
			"stats":{"field":"age"}
		}
	},
	"size":0
}

桶聚合查询

桶聚和相当于sql 中的 group by 语句

terms 聚合,分组统计

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
	"aggs":{ 
		"age_groupby":{
						"terms":{"field":"age"}
					}
		},
	"size":0
}

在 terms 分组下再进行聚合

在 Postman 中,向ES 服务器发 GET 请求 :http://127.0.0.1:9200/student/_search

{
	"aggs":{ 
		"age_groupby":{
						"terms":{"field":"age"}
					}
		},
	"size":0
}

Elastic 进阶

核心概念

索引(Index

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

能搜索的数据必须索引,这样的好处是可以提高查询速度,比如:新华字典前面的目录就是索引的意思,目录可以提高查询速度。

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

类型(Type)

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

一个类型是你的索引的一个逻辑上的分类/分区,其语义完全由你来定。通常,会为具有一组共同字段的文档定义一个类型。不同的版本,类型发生了不同的变化

版本Type
5.x支持多种 type
6.x只能有一种 type
7.x默认不再支持自定义索引类型(默认类型为:_doc)
文档(Document)

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

比如:你可以拥有某一个客户的文档,某一个产品的一个文档,当然,也可以拥有某个订单的一个文档。文档以 JSON(Javascript Object Notation)格式来表示,而JSON 是一个到处存在的互联网数据交互格式。

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

字段(Field)

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

映射(Mapping)

mapping 是处理数据方式规则方面做一些限制,如:某个字段的数据类型、默认值、分析器、是否被索引等等。这些都是映射里面可以设置的,其它就是处理ES 里面数据的一些使用规则设置也叫做映射,按着最优规则处理数据对性能提高很大,因此才需要建立映射, 并且需要思考如何建立映射才能对性能更好。

分片(Shards)

一个索引可以存储超出单个节点硬件限制的大量数据。比如,一个具有 10 亿文档数据的索引占据 1TB 的磁盘空间,而任一节点都可能没有这样大的磁盘空间。或者单个节点处理搜索请求,响应太慢。为了解决这个问题,Elasticsearch 提供了将索引划分成多份的能力, 每一份就称之为分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点 上

分片很重要,主要有两方面的原因:

1) 允许你水平分割 / 扩展你的内容容量。

2) 允许你在分片之上进行分布式的、并行的操作,进而提高性能/吞吐量。

至于一个分片怎样分布,它的文档怎样聚合和搜索请求,是完全由 Elasticsearch 管理的, 对于作为用户的你来说,这些都是透明的,无需过分关心。

被混淆的概念是,一个 Lucene 索引 我们在 Elasticsearch 称作 分片 。 一个Elasticsearch索引 是分片的集合。 当Elasticsearch 在索引中搜索的时候, 他发送查询到每一个属于索引的分片(Lucene 索引),然后合并每个分片的结果到一个全局的结果集。

副本(Replicas)

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

复制分片之所以重要,有两个主要原因:

  • 在分片/节点失败的情况下,提供了高可用性。因为这个原因,注意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的。

  • 扩展你的搜索量/吞吐量,因为搜索可以在所有的副本上并行运行。

总之,每个索引可以被分成多个分片。一个索引也可以被复制 0 次(意思是没有复制) 或多次。一旦复制了,每个索引就有了主分片(作为复制源的原来的分片)和复制分片(主分片的拷贝)之别。分片和复制的数量可以在索引创建的时候指定。在索引创建之后,你可以在任何时候动态地改变复制的数量,但是你事后不能改变分片的数量。默认情况下,

Elasticsearch 中的每个索引被分片 1 个主分片和 1 个复制,这意味着,如果你的集群中至少

有两个节点,你的索引将会有 1 个主分片和另外 1 个复制分片(1 个完全拷贝),这样的话,每个索引总共就有 2 个分片,我们需要根据索引需要确定分片个数。

分配(Allocation)

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

系统架构

一个运行中的 Elasticsearch 实例称为一个节点,而集群是由一个或者多个拥有相同cluster.name 配置的节点组成, 它们共同承担数据和负载的压力。当有节点加入集群中或者从集群中移除节点时,集群将会重新平均分布所有的数据。

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

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

分片控制

写流程9/14

新建、索引和删除 请求都是 写 操作, 必须在主分片上面完成之后才能被复制到相关的副本分片

新建,索引和删除文档所需要的步骤顺序

  1. 客户端向 Node 1 发送新建、索引或者删除请求。

  2. 节点使用文档的 _id 确定文档属于分片 0 。请求会被转发到 Node 3,因为分片 0 的主分片目前被分配在 Node 3 上。

  3. Node 3 在主分片上面执行请求。如果成功了,它将请求并行转发到 Node 1 和 Node 2 的副本分片上。一旦所有的副本分片都报告成功, Node 3 将向协调节点报告成功,协调节点向客户端报告成功。

在客户端收到成功响应时,文档变更已经在主分片和所有副本分片执行完成,变更是安全的。有一些可选的请求参数允许您影响这个过程,可能以数据安全为代价提升性能。这些选项很少使用,因为Elasticsearch 已经很快,但是为了完整起见,请参考下面表格:

参数含义
consistencyconsistency,即一致性。在默认设置下,即使仅仅是在试图执行一个_写_操作之前,主分片都会要求 必须要有 规定数量(quorum)(或者换种说法,也即必须要有大多数)的分片副本处于活跃可用状态,才会去执行_写_操作(其中分片副本可以是主分片或者副本分片)。这是为了避免在发生网络分区故障(network partition)的时候进行_写_操作,进而导致数据不一致。规定数量_即: int( (primary + number_of_replicas) / 2 ) + 1 consistency 参数的值可以设为 one (只要主分片状态 ok 就允许执行_写_操作),all(必须要主分片和所有副本分片的状态没问题才允许执行_写_操作), 或quorum 。默认值为 quorum , 即大多数的分片副本状态没问题就允许执行_写 操作。 注意,规定数量 的计算公式中 number_of_replicas 指的是在索引设置中的设定 副本分片数,而不是指当前处理活动状态的副本分片数。如果你的索引设置中指
定了当前索引拥有三个副本分片,那规定数量的计算结果即: int( (primary + 3 replicas) / 2 ) + 1 = 3 如果此时你只启动两个节点,那么处于活跃状态的分片副本数量就达不到规定数量,也因此您将无法索引和删除任何文档。
timeout如果没有足够的副本分片会发生什么? Elasticsearch 会等待,希望更多的分片出 现。默认情况下,它最多等待 1 分钟。 如果你需要,你可以使用 timeout 参数使它更早终止: 100 100 毫秒,30s 是 30 秒。

新索引默认有 1 个副本分片,这意味着为满足规定数量应该需要两个活动的分片副本。 但是,这些默认的设置会阻止我们在单一节点上做任何事情。为了避免这个问题,要求只有当 number_of_replicas 大于 1 的时候,规定数量才会执行。

读流程

我们可以从主分片或者从其它任意副本分片检索文档

从主分片或者副本分片检索文档的步骤顺序

  1. 客户端向 Node 1 发送获取请求。

  2. 节点使用文档的 _id 来确定文档属于分片 0 。分片 0 的副本分片存在于所有的三个节点上。 在这种情况下,它将请求转发到 Node 2 。

  3. Node 2 将文档返回给 Node 1 ,然后将文档返回给客户端。

在处理读取请求时,协调结点在每次请求的时候都会通过轮询所有的副本分片来达到负载均衡。在文档被检索时,已经被索引的文档可能已经存在于主分片上但是还没有复制到副本分片。 在这种情况下,副本分片可能会报告文档不存在,但是主分片可能成功返回文档。 一旦索引请求成功返回给用户,文档在主分片和副本分片都是可用的。

更新流程

部分更新一个文档结合了先前说明的读取和写入流程:

部分更新一个文档的步骤如下

  1. 客户端向 Node 1 发送更新请求。

  2. 它将请求转发到主分片所在的 Node 3 。

  3. Node 3 从主分片检索文档,修改 _source 字段中的 JSON ,并且尝试重新索引主分片的文档。如果文档已经被另一个进程修改,它会重试步骤 3 ,超过 retry_on_conflict 次后放弃。

  4. 如果 Node 3 成功地更新文档,它将新版本的文档并行转发到 Node 1 和 Node 2 上的副本分片,重新建立索引。一旦所有副本分片都返回成功, Node 3 向协调节点也返回成功,协调节点向客户端返回成功。

当主分片把更改转发到副本分片时, 它不会转发更新请求。 相反,它转发完整文档的新版本。请记住,这些更改将会异步转发到副本分片,并且不能保证它们以发送它们相同的顺序到达。 如果 Elasticsearch 仅转发更改请求,则可能以错误的顺序应用更改,导致得到损坏的文档。

分片原理

倒排索引

Elasticsearch 使用一种称为倒排索引的结构,它适用于快速的全文搜索。

见其名,知其意,有倒排索引,肯定会对应有正向索引。正向索引(forward index), 反向索引(inverted index)更熟悉的名字是倒排索引。

所谓的正向索引,就是搜索引擎会将待搜索的文件都对应一个文件 ID,搜索时将这个ID 和搜索关键字进行对应,形成 K-V 对,然后对关键字进行统计计数

但是互联网上收录在搜索引擎中的文档的数目是个天文数字,这样的索引结构根本无法满足实时返回排名结果的要求。所以,搜索引擎会将正向索引重新构建为倒排索引,即把文件 ID 对应到关键词的映射转换关键词到文件ID 的映射每个关键词都对应着一系列的文件, 这些文件中都出现这个关键词。

一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,有一个包含它的文档列表。例如,假设我们有两个文档,每个文档的 content 域包含如下内容:

The quick brown fox jumped over the lazy dog

Quick brown foxes leap over lazy dogs in summer

为了创建倒排索引,我们首先将每个文档的 content 域拆分成单独的 词(我们称它为 词条或 tokens ),创建一个包含所有不重复词条的排序列表,然后列出每个词条出现在哪个文档。结果如下所示:

现在,如果我们想搜索 quick brown ,我们只需要查找包含每个词条的文档:

两个文档都匹配,但是第一个文档比第二个匹配度更高。如果我们使用仅计算匹配词条数量的简单相似性算法,那么我们可以说,对于我们查询的相关性来讲,第一个文档比第二个文档更佳。

但是,如果我们对搜索的字符串使用与 content 域相同的标准化规则,会变成查询+quick +fox,这样两个文档都会匹配!分词和标准化的过程称为分析

这非常重要。你只能搜索在索引中出现的词条,所以索引文本和查询字符串必须标准化为相同的格式。

核心概念:

词条:索引中最存储和查询单元

词典:字典,词条的集合,B+,HashMap

倒排表:

文档搜索

早期的全文检索会为整个文档集合建立一个很大的倒排索引并将其写入到磁盘。 一旦新的索引就绪,旧的就会被其替换,这样最近的变化便可以被检索到。

倒排索引被写入磁盘后是不可改变的:它永远不会修改。不变性有重要的价值:

l 不需要锁。如果你从来不更新索引,你就不需要担心多进程同时修改数据的问题。

l 一旦索引被读入内核的文件系统缓存,便会留在哪里,由于其不变性。只要文件系统缓存中还有足够的空间,那么大部分读请求会直接请求内存,而不会命中磁盘。这提供了很大的性能提升。

l 其它缓存(像 filter 缓存),在索引的生命周期内始终有效。它们不需要在每次数据改变时被重建,因为数据不会变化。

l 写入单个大的倒排索引允许数据被压缩,减少磁盘 I/O 和 需要被缓存到内存的索引的使用量。

当然,一个不变的索引也有不好的地方。主要事实是它是不可变的! 你不能修改它。如果你需要让一个新的文档 可被搜索,你需要重建整个索引。这要么对一个索引所能包含的数据量造成了很大的限制,要么对索引可被更新的频率造成了很大的限制。

动态更新索引

如何在保留不变性的前提下实现倒排索引的更新?

答案是: 用更多的索引。通过增加新的补充索引来反映新近的修改,而不是直接重写整个倒排索引。每一个倒排索引都会被轮流查询到,从最早的开始查询完后再对结果进行合并。

Elasticsearch 基于 Lucene, 这个 java 库引入了按段搜索的概念。 每一 段 本身都是一个倒排索引, 但索引在 Lucene 中除表示所有段的集合外, 还增加了提交点的概念 — 一个列出了所有已知段的文件

按段搜索会以如下流程执行:

新文档被收集到内存索引缓存

ntent 域相同的标准化规则,会变成查询+quick +fox,这样两个文档都会匹配!分词和标准化的过程称为分析

这非常重要。你只能搜索在索引中出现的词条,所以索引文本和查询字符串必须标准化为相同的格式。

核心概念:

词条:索引中最存储和查询单元

词典:字典,词条的集合,B+,HashMap

倒排表:

文档搜索

早期的全文检索会为整个文档集合建立一个很大的倒排索引并将其写入到磁盘。 一旦新的索引就绪,旧的就会被其替换,这样最近的变化便可以被检索到。

倒排索引被写入磁盘后是不可改变的:它永远不会修改。不变性有重要的价值:

l 不需要锁。如果你从来不更新索引,你就不需要担心多进程同时修改数据的问题。

l 一旦索引被读入内核的文件系统缓存,便会留在哪里,由于其不变性。只要文件系统缓存中还有足够的空间,那么大部分读请求会直接请求内存,而不会命中磁盘。这提供了很大的性能提升。

l 其它缓存(像 filter 缓存),在索引的生命周期内始终有效。它们不需要在每次数据改变时被重建,因为数据不会变化。

l 写入单个大的倒排索引允许数据被压缩,减少磁盘 I/O 和 需要被缓存到内存的索引的使用量。

当然,一个不变的索引也有不好的地方。主要事实是它是不可变的! 你不能修改它。如果你需要让一个新的文档 可被搜索,你需要重建整个索引。这要么对一个索引所能包含的数据量造成了很大的限制,要么对索引可被更新的频率造成了很大的限制。

动态更新索引

如何在保留不变性的前提下实现倒排索引的更新?

答案是: 用更多的索引。通过增加新的补充索引来反映新近的修改,而不是直接重写整个倒排索引。每一个倒排索引都会被轮流查询到,从最早的开始查询完后再对结果进行合并。

Elasticsearch 基于 Lucene, 这个 java 库引入了按段搜索的概念。 每一 段 本身都是一个倒排索引, 但索引在 Lucene 中除表示所有段的集合外, 还增加了提交点的概念 — 一个列出了所有已知段的文件

  • 33
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Elasticsearch是一个开源的分布式搜索和分析引擎,基于Apache Lucene搜索库构建。它被广泛用于日志分析、全文检索、业务监控等场景。 在Java中使用Elasticsearch可以通过Java API来操作。以下是一个简单的示例: 1. 在pom.xml文件中添加Elasticsearch依赖: ``` <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>7.9.2</version> </dependency> ``` 2. 创建Elasticsearch客户端: ``` RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("localhost", 9200, "http"))); ``` 3. 创建索引: ``` CreateIndexRequest request = new CreateIndexRequest("my_index"); client.indices().create(request, RequestOptions.DEFAULT); ``` 4. 添加文档: ``` IndexRequest request = new IndexRequest("my_index"); request.id("1"); String jsonString = "{" + "\"name\":\"John\"," + "\"age\":30," + "\"city\":\"New York\"" + "}"; request.source(jsonString, XContentType.JSON); IndexResponse response = client.index(request, RequestOptions.DEFAULT); ``` 5. 搜索文档: ``` SearchRequest request = new SearchRequest("my_index"); SearchSourceBuilder builder = new SearchSourceBuilder(); builder.query(QueryBuilders.matchQuery("name", "John")); request.source(builder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); ``` 这是一个简单的示例,实际使用中还可以使用更多的API来操作Elasticsearch

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值