随着业务的增长,数据与日俱增,这时为用户带来丰富的、便捷的搜索功能就迫在眉睫了。传统的数据库在处理文本搜索、模糊查询、海量数据统计分析的时候总会力不从心,所以在处理这些复杂的搜索需求时,我们更倾向于使用 Elasticsearch 搜索引擎。
Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析引擎,其在 DB-Engines “兵器”排行榜中长期位列第一。
Es为什么检索效率高
- Elasticsearch通过其独特的倒排索引、分片和副本机制、缓存和缓存预热、近似搜索算法以及分布式搜索和实时分析的能力,实现了高效的数据检索和处理,从而提供了比传统数据库更快的搜索效率
让我们来了解一下这些基础概念吧!
集群(Cluster)
-
如下图,由多个协同工作的 ES 实例组合成的集合称为集群,图中节点 Node1、Node2、Node3 分别运行了一个 ES
实例,它们组成一个集群。分布式的 ES 集群可以存储海量的数据,也可以从容地面对更高的并发量。
-
集群也是一种提供无间断服务的方案,得益于分布式系统的架构设计,使得 ES 拥有高可用性和可扩展性。
-
高可用性,分为服务可用性、数据可用性。 服务可用性,在有部分节点挂掉的情况下系统还可以对外提供服务
-
数据可用性,部分节点挂掉,并且这些节点的数据无法恢复的情况下,也能保证数据不丢失。
-
可扩展性,当并发量提升,或者数据量增多的情况下,可以通过增加节点数来解决问题.
节点(Node)
-
单个 ES 的服务实例叫做节点,本质上就是一个 Java 进程。每个实例都有自己的名字,就是上一节配置里的 ‘node.name’
设置的内容。为了标识每个节点,每个节点启动后都会分配一个 UID,存储在 data
目录。各个节点受到集群的管理,我们可以通过增加或者减少节点来达到扩容或减容的目的。集群里的节点是有分类的,就好像一家公司的不同部门,负责不同的业务和工作,主要的节点类型如下。
-
主节点(Master)。主节点在整个集群是唯一的,Master 从有资格进行选举的节点(MasterEligible)中选举出来。主节点主要负责管理集群变更、元数据的更改。如果要类比的话,主节点像是公司的总经办。
-
数据节点(Data Node)。其负责保存数据,要扩充存储时候需要扩展这类节点。数据节点还负责执行数据相关的操作,如:搜索、聚合、CURD 等。所以对节点机器的 CPU、内存、I/O 要求都比较高。
-
协调节点(CoordinatingNode)。负责接受客户端的请求,将请求路由到对应的节点进行处理,并且把最终结果汇总到一起返回给客户端。因为需要处理结果集和对其进行排序,需要较高的
CPU 和内存资源。 -
预处理节点(Ingest Node)。预处理操作允许在写入文档前通过定义好的一些processors(处理器)和管道对数据进行转换。默认情况下节点启动后就是预处理节点。
-
部落节点(Tribe Node)。部落节点可以连接到不同集群,并且支持将这些集群当成一个单独的集群处理。但它在未来的版本中将会被淘汰。
-
Hot & Warm Node。不同硬件配置的 Data Node,用来实现 Hot & Warm架构的节点,利于降低集群部署成本。例如,在硬件资源好的机器中部署 Hot 类型的数据节点,而在硬件资源一般的机器上部署 Warm Node 节点。
这里延深了一个只是点node.name
node.name使用来做什么的呢
node.roles: [ master, data ] //设置节点为 master 候选节点和数据节点
-
如上示例,node.roles 的值是一个数组,说明一个节点可以有多个角色。node.roles 的可选项如下:
master,master 候选节点,master 将会从这些节点中选取出来。
voting_only,参与 master 选举的节点,其只有投票权限,当不会成为 master。
data,我们最熟悉的一类数据节点。保存文档数据的 shard 将分配到 data 节点中保存。
data_content,此角色的节点会处理用户创建的文档内容,如书本信息,歌曲信息这类数据。可以处理 CRUD、数据搜索和聚合等。
data_hot,此角色的节点会根据数据写入 ES 的时间存储时序数据,例如日志数据,data_hot 节点对数据读写要求快速,应当使用 SSD 存储。
data_warm,data_warm 节点会存储不会经常更新但是仍然被查询的数据,相对于 data_hot,其查询的频率要低。
data_cold,很少再被读取的数据可以存储在 data_cold,此类节点的数据是只读的。
data_frozen,专门用于存储 partially mounted indices 的数据节点。
ingest, 预处理数据的节点。
ml, 提供机器学习的功能,此类节点运行作业并处理机器学习 API 请求。
remote_cluster_client,充当跨集群客户端并连接到其他集群。
transform,运行 transforms 并处理 transform API 请求。
机智的你会发现,上述类型中并没有协调节点的类型选项,那怎么设置一个节点为协调节点呢?其实每个节点本身就是一个协调节点,而你一定要指定一个节点为协调节点的话,可以这样设置:
node.roles: [ ] //对,就是啥都不写即可
分片(Shard)
-
分片的概念其实很好理解,试想一下如果家里的书多到一个箱子装不下,是不是要找另外一个箱子来装?这些书就好比海量的数据,一台机器存不下,就放到多台机器来存储。
-
如上图,数据集 Data 按某些规则分为 4 个部分,然后被存储到 4 个节点上面(一个节点一个分片)。一般来说,面对海量数据的时候,分布式系统可以通过增加机器数量来进行水平扩展。所以,系统需要将数据分成多个小块数据,并且尽量均匀地分配到各个机器上,然后可以通过某种策略找到对应数据块所在的位置。分片(Shard)是 ES 底层基本的读写单元,分片是为了分割巨大的索引数据,让读写可以由多台机器来完成,从而提高系统的吞吐量。
副本(Replica)
-
为了保证数据可靠性,一般分布式系统都会对数据进行冗余备份,这个备份也就是副本了。ES将数据副本分为主从两类型:主分片(primary shard)和副分片(replica shard) 。在写入的过程中,先写主分片,成功后并发写副分片,在数据恢复时以主分片为主。多个副本除了可以保证数据可靠性外,还有一个好处是可以承担系统的读负载。
-
#创建 mapping 的时候定义好分片和副本数量。
PUT books { "mappings": { "properties": { "book_id": { "type": "keyword" }, "name": { "type": "text" } } }, "settings": { "number_of_shards": 2, # 定义了 2 个分片 "number_of_replicas": 2 # 定义了每个分片 2 个副分片 } }
集群健康状态
-
通过集群的健康状态,我们可以了解集群是不是出现问题了。 集群健康状态有以下 3 种。
Green,集群处于健康状态,所有的主分片和副本分片都正常运行。
Yellow,所有的主分片都运行正常,但是有部分副本分片不正常,意味着可能存在单点故障的风险(如果部分主分片没有备份了,一旦这个主分片数据丢失,将导致这些数据永久丢失)。如果集群只有 3 个数据节点,但是分配了 4 个副本(主分片 + 副本分片的总数),这个时候有一个副本无法分配的,因为相同的两份数据不应该被分配到同一个节点上。
Red,有部分主分片没有正常运行。
需要注意的是,每个索引也有这三种状态,如果索引丢失了一个副本分片,那么这个索引和集群的状态都变为 Yellow 状态,但是其他索引的的状态仍为 Green。
倒排索引
- 倒排索引之所以快,是因为它通过将文档中的每个单词映射到包含该单词的文档列表中,从而可以快速查找包含特定单词的文档,而无需扫描整个文档集合。在汉语中,词的数量是有限的,通过这些词组成的文章千千万万,因此通过索引来查找文章会非常快速。
倒排索引的组成和实现原理
- 倒排索引主要由三个部分组成:Term Index、Term Dictionary 和 Posting
List。在Lucene的实现中,词项索引(Term Index)用于解决词项方面的问题,而使用Roaring
Bitmaps、跳表等技术进行快速求交集。这种数据结构的设计使得倒排索引能够处理大量的数据并支持实时搜索和分析。
近实时系统
- ES 是一个近实时系统,我们写入的数据默认的情况下会在 1 秒后才能被查询到。ES 每秒都会把缓存中的数据写入到 Segment
文件中(写入到 Segment 中才能被检索),然后根据某些规则进行刷盘,并且合并这些
Segment。所以需要注意的是,不能在写入数据成功后,立刻进行查询,这个时候可能会出现查询不到数据或者获取到旧数据的情况。
Lucene 与 ES 的关系
-
Lucene 是一个用于全文检索的开源项目,ES 在搜索的底层实现上用的就是 Lucene。你可以简单认为,ES 就是在 Lucene上增加了分布式特性的系统服务。
-
Lucene 也有索引,那 Lucene 的索引和 ES 的索引是个怎么样的关系呢?其实 ES 上的分片(Shard)就是一个完整的
Lucene 索引。
我们已经了解了它的基本原理 现在我们实现一下基本使用方法
文档基本操作接口有:
- 新建文档,提供了索引文档(Index doc)和创建文档(create doc)两种方式。
- 通过文档 ID 获取文档,通过 Get API 来获取文档内容。
- 批量获取文档,使用 MGET API 来批量获取文档。
- 更新文档,使用 Update API 来更新文档。
- 删除文档,使用 Delete API 来删除文档。
- 批量操作文档,使用 Bulk API 来批量操作文档。
索引管理
#创建 books 索引
PUT books
{
"mappings": {
"properties": {
"book_id": {
"type": "keyword"
},
"name": {
"type": "text"
},
"author": {
"type": "keyword"
},
"intro": {
"type": "text"
}
}
},
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}
#返回结果
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "books"
}
在 Kibana 中运行上述示例,可以创建 books 索引。books 索引包含 book_id(书本 ID)、name(书本名字)、author(作者)、intro(简介)四个字段,可以满足我们现阶段的需求。其中 books 索引还有 3 个分片和 1 个副本备份。
如果你之前创建过 books 索引的话,这里再次创建会报错,所以你需要先将之前的索引删除,然后再重新创建。
新建文档
-
这天书店进了一批电子书,管理员需要把这些书录入到 ES 中。ES 提供了两种创建文档的方式,一种是使用 Index API索引文档,一种是使用 Create API 创建文档。
# 使用 Index API 索引文档 PUT books/_doc/1 { "book_id": "4ee82462", "name": "深入Linux内核架构", "author": "Wolfgang Mauerer", "intro": "内容全面深入,领略linux内核的无限风光。" } # 结果 { "_index" : "books", "_type" : "_doc", "_id" : "1", "_version" : 1, "result" : "created", "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "_seq_no" : 0, "_primary_term" : 1 }
# 使用 PUT 的方式创建文档 PUT books/_create/2 { "book_id": "4ee82463", "name": "时间简史", "author": "史蒂芬霍金", "intro": "探索时间和空间核心秘密的引人入胜的故事。" } # PUT 方式返回的结果 { "_index" : "books", "_type" : "_doc", "_id" : "2", "_version" : 1 ...... } # 使用 POST 的方式,不需要指定文档 ID, 系统自动生成 POST books/_doc { "book_id": "4ee82464", "name": "时间简史(插画版)", "author": "史蒂芬霍金", "intro": "用精美的插画带你探索时间和空间的核心秘密" } # POST 方式返回的结果 { "_index" : "books", "_type" : "_doc", "_id" : "LfwVtH0BxOuNtEd4yM4F", "_version" : 1 ...... }
获取文档
使用 GET API 获取单个文档
通过书本的 ID 获取书本的信息时可以使用 GET API 来实现,其示例如下:
# 使用 GET API 获取单个文档的内容
GET books/_doc/1
# 结果
{
"_index" : "books",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
"book_id" : "4ee82462",
"name" : "深入Linux内核架构",
"author" : "Wolfgang Mauerer",
"intro" : "内容全面深入,领略linux内核的无限风光。"
}
}
GET API 提供了多个参数
这里是从官方文档拔下来 各位想看更多参数可以去看官方文档
更新文档
# 更新文档
POST books/_update/2
{
"doc": {
"name":"时间简史(视频版)",
"intro": "探索时间和空间核心秘密的引人入胜的视频故事。"
}
}
#结果
{
"_index" : "books",
"_type" : "_doc",
"_id" : "2",
"_version" : 3,
"result" : "updated",
......
}
删除文档
# 删除文档 2
DELETE books/_doc/2
# 结果
{
"_index" : "books",
"_type" : "_doc",
"_id" : "2",
"_version" : 4,
"result" : "deleted",
......
}
批量操作文档
当我们需要写入多个文档的时候,如果每写一个文档就发起一个请求的话,多少有点浪费。这个时候我们可以使用 Bulk API 来批量处理文档。
Bulk API 支持在一次调用中操作不同的索引,使用时可以在 Body 中指定索引也可以在 URI 中指定索引。而且还可以同时支持 4 中类型的操作:
- Index
- Create
- Update
- Delete
下面就举一个批量添加的例子 剩下的大家可以看官方文档 毕竟学习还是官方文档的最准确
# 在 Bulk API 中同时使用多种操作类型的实例
POST _bulk
{ "index" : { "_index" : "books", "_id" : "1" } }
{ "book_id": "4ee82462","name": "深入Linux内核架构","author": "Wolfgang Mauerer","intro": "内容全面深入,领略linux内核的无限风光。" }
{ "delete" : { "_index" : "books", "_id" : "2" } }
{ "create" : { "_index" : "books", "_id" : "3" } }
{ "book_id": "4ee82464","name": "深入Linux内核架构第三版","author": "Wolfgang Mauerer","intro": "内容全面深入,再次领略linux内核的无限风光。" }
{ "update" : {"_index" : "books", "_id" : "4"} } # 指定操作类型、索引、文档 id
{ "doc" : {"intro" : "书本的内容非常好,值得一看"} } # 指定文档内容
# 结果
{
"items" : [
{
"index" : {
"_id" : "1",
"result" : "created",
......
}
},
{
"delete" : {
"_id" : "2",
"result" : "deleted",
......
}
},
{
"create" : {
"_id" : "3",
"result" : "created",
......
}
},
{
"update" : {
"_id" : "4",
"status" : 404,
......
}
}
]
}
标签:基本概念,索引,books,文档,分片,id,Elasticsearch,节点,底层
From: https://blog.csdn.net/2301_81352793/article/details/142529441