一文带你了解Elasticsearch底层逻辑

1. 什么是Elasticsearch

Elasticsearch 是一个分布式、RESTful 风格的搜索引擎,基于 Apache Lucene。它提供了一个高度可扩展的全文搜索引擎,具备近实时的搜索能力。Elasticsearch 适用于各种用例,包括文档搜索、日志和指标数据分析、应用程序性能监控、线程分析等。

Elasticsearch最根本的使用场景是搜索引擎,比如说我们有一堆文档,我们想要搜索到所有包含"树獭叔叔"的文档,这个时候就可以借助Elasticsearch了。

2. Segment

Segment是Lucene的基本存储单元,Lucene是es依赖的底层开源技术,一个Segment单元由以下四部分组成:

2.1. 倒排索引 Inverted Index

倒排索引是一种用于快速全文检索的数据结构,它将文档和词语进行关联,从而使搜索引擎能够快速查找到包含特定词语的文档。倒排索引逆转了传统索引(如在书本的后面查找关键词)的关系,即它将每个词和包含该词的文档列表关联起来。

假设我们有以下三个文档集:

  1. "Elasticsearch is a search engine"
  2. "Elasticsearch is extremely powerful"
  3. "Search engines are fascinating"

建立倒排索引的步骤如下:

  1. 分词(Tokenization)
    • 第一步是将文档分解为独立的词语。例如:
      • Doc 1: ["Elasticsearch", "is", "a", "search", "engine"]
      • Doc 2: ["Elasticsearch", "is", "extremely", "powerful"]
      • Doc 3: ["Search", "engines", "are", "fascinating"]
  1. 去重和排序
    • 过滤掉重复的词,并对词语进行排序。例如:
      • 总词汇表: ["Elasticsearch", "Search", "a", "are", "engine", "engines", "extremely", "fascinating", "is", "powerful"]
  1. 建立倒排列表
    • 对每个词建立一个文档列表,记录包含该词的所有文档。结果如下:
词语 文档列表
Elasticsearch [1, 2]
Search [1, 3]
a [1]
are [3]
engine [1]
engines [3]
extremely [2]
fascinating [3]
is [1, 2]
powerful [2]

在Elasticsearch中,倒排索引是Lucene库的一部分。Elasticsearch 使用Lucene来管理和查询这些倒排索引,从而实现高效的全文搜索。下面是如何在Elasticsearch中处理倒排索引的一些关键步骤:

  1. 文档的分词和分析
    • 当文档被索引时,Elasticsearch会先将文档内容进行分词和分析。这通常包括分词、去除停用词(如"a", "is", "the"等)以及将单词归一化(例如将单词转换为小写)。
  1. 建立倒排索引
    • 分词过程生成的词语会被添加到倒排索引中,每个词语关联到包含该词的文档列表中。
  1. 查询和匹配
    • 当进行搜索时,Elasticsearch 会解析查询内容,找到相关的词语,并使用倒排索引快速定位到包含这些词语的文档。然后,根据查询的类型,可能还会计算每个文档的相关度得分,从而排序返回结果。

2.2. Term Index

以上面的倒表索引表为例:

词语(Term) 文档列表
Elasticsearch [1, 2]
Search [1, 3]
a [1]
are [3]
engine [1]
engines [3]
extremely [2]
fascinating [3]
is [1, 2]
powerful [2]

其实我们不难发现,如果是对文章进行分词,那么词项的数量级是很大的,这个时候如果是通过遍历的方式查找每一个词项,那么他的效率是很低的,因此,我们可以按照字典序对词项进行排序,查找的时候就可以借助二分法优化查询效率。

就算这样,我们在查找时还是会存在问题,因为词项的数据量很大,如果直接把词项表存在内存中,很可能有内容问题,因此,我们可以借助字典数的数据结构维护整个词项表,减少内存占用的同时提高查询效率。

2.3. Doc Values

Doc Values本质是通过冗余存储达到更快的排序与搜索,例如我们可以把商品文档的价格属性进行冗余存储,这样我们在依靠价格进行排序时,就能更快的完成排序与搜索过程。

2.4. Stored Fields

文档数据存储的物理单元。

3. Lucene

前面我们已经介绍了Lucene的基本单元segment,这里就要介绍一下Lucene是如何对segment进行调度了。

前面提到,segment是由多个存储结构共同构成的,如果更新的话,就需要对多个结构进行更新,为了满足高性能读写的需求,我们可以在segment的维度进行读写分离,老segment只负责读,新写入的数据生成新的segment,随着segment越来越多,可以定期对小的segment进行merge,合并成大的segment,这就是Lucene中对segment的基本调度规则。

4. 如何基于Lucene实现高可用

如果大量的数据都写在一个Lucene单元中,必然会导致读写操作的竞争,那么,有什么办法提高奇并发读写的性能吗?当然有,就像数据库的分库分表一样,es也有自己的分库分表:

4.1. Index name

可以根据数据类型对Lucene进行分类,分成多个Index name,根据不同的Index name路由道不同类型的数据。例如,将娱乐文章和学术文章区分开(类似于数据库的垂直分表)。

4.2. Shard分片

可以将同一个Index name中的数据通过hash的方式打散到不同的分片上,之后部署到不同的机器,进一步提升可用性。(类似于水平分表)

我们还可以对shard分片进行主从冗余部署,提高可靠性。

5. ES的结构

5.1.1. Document

ES是面向文档的,索引、检索、排序、过滤的对象都是文档。

它是ES中可搜索的最小单位,使用JSON作为文档的格式。

文档中包含着若干个字段,类似于关系型数据库中的一行数据。

ruby 复制代码
{
	"_index": "user",
	//"_type": "_doc",
	"_id": "1",
	"_version": 1,
	"_seq_no": 26,
	"_primary_term": 4,
	"found": true,
	"_source": {
		"name": "John Doe",
		"age": 21,
		"birthday": "2000/01/01",
		"interests": ["running", "movie"]
	}
}

5.1.2. Mapping

文档中的每个字段都有自己的类型,可以是字符串、数值、日期等。通过 Mapping来设定字段的名称和类型, 类似于关系型数据库中的 Schema。比如上面那个文档对应的Mapping 如下:

sql 复制代码
user {
	"mappings": {
		"properties": {
			"name": {
				"type": "string"
			},
			"age": {
				"type": "short"
			},
			"birthday": {
				"type": "date"
			},
			"interests": {
				"type": "string"
			}
		}
	}
}
相关推荐
NiNg_1_2342 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
Chrikk3 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*3 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue3 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man3 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
customer085 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
Yaml46 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
小码编匠7 小时前
一款 C# 编写的神经网络计算图框架
后端·神经网络·c#