【搜索引擎】原理层面:Elasticsearch

一、前言

ES两个作用:
1、组成成为ELK,用来收集和处理日志,nginx、apache、message、secrue、mysql的日志;
2、ES单独使用,用来做搜索引擎,特别是数据量比较大的,不用直接查库的,电商网站的搜索,Solr也可以

二、ElasticSearch的引入

2.1 ElasticSearch定义

定义:Elasticsearch 是一个实时的分布式存储、搜索、分析的引擎。
五个关键字:实时、分布式、存储、搜索、分析
问题1:Elasticsearch是如何做到实时的,
问题2:Elasticsearch的架构分布式的。
问题3:Elasticsearch是怎么存储、搜索和分析的

2.2 ElasticSearch取代数据库like查找的现实需求(三个理由)

为什么要使用Elasticsearch?mysql数据库也能做到 实时(高可用,实施读写)、存储(存储数据)、搜索(like查找)、分析(对数据分析,具体分析因人而异)

ES的优势在于高效的模糊查询(注意两个字:高效、模糊查询),mysql也可以使用like关键字和% 模糊查询,但是,这种方式有三个缺陷:
第一,效率低:like + % 这类的查询是不走索引的,不走索引意味着:只要你的数据库的量很大(1亿条),你的查询肯定会是秒级别的;
第二,无法控制数据量,返回大量数据:使用 like + % 即便给你从数据库根据模糊匹配查出相应的记录了,那往往会返回大量的数据给你,往往你需要的数据量并没有这么多,可能50条记录就足够了;
第三,无法错别字搜索:用户输入的内容往往并没有这么的精确,比如我从Google输入ElastcSeach(打错字),但是Google还是能估算我想输入的是Elasticsearch。

Elasticsearch是专门做搜索的,可以解决mysql搜索的三个问题:
第一,搜索速度很快:Elasticsearch对模糊搜索非常擅长
第二,原生就支持排序:从Elasticsearch搜索到的数据可以根据评分过滤掉大部分的,只要返回评分高的给用户就好了
第三,能匹配有相关性的记录:没有那么准确的关键字也能搜出相关的结果

三、ElasticSearch的特征

3.1 为什么Elasticsearch为什么可以实现快速的“模糊匹配”或者快速的“相关性查询”? 标准答案:倒排索引 + 分词器

3.1.1 倒排索引

问题:为什么Elasticsearch为什么可以实现快速的“模糊匹配”或者快速的“相关性查询”?
标准答案:ES分词器 + 倒排索引,解释:ES分词器就是写入数据到Elasticsearch的时候会进行分词,倒排索引就是拿着分词这种不完全条件(对应0-n个位置)去找位置,然后找记录

正向索引定义:根据“完整的条件”查找一条记录叫做正向索引;
函数表示:即给定x仅对于唯一一个确定y值;
实际:如一本书的章节目录就是正向索引,通过目录找到唯一确定的页码。
倒排索引定义:根据某个词(不完整的条件)再查找对应记录,叫做倒排索引;
函数表示:即给定的y值有0-n个对应的x值,ES就是根据倒排索引查找的;
实际:如随便打开一个书上的一页,ES会记录各个词语和各个词语对应的位置,ES根据各个词为它找他对应的目录,比如算法 ->2,13,42,56,解释:“算法”这个词肯定是在第二页、第十三页、第四十二页、第五十六页出现过。
ES 使用分词器+倒排索引
再看下面的图,好好体会一下:

3.1.2 分词器

问题:ES如何实现人类语言的分词(中文、英文、俄罗斯文等)?
标准答案:这个问题问的不清楚,一定要注意,ES不完成分词功能,ES是一个索引库,是一个搜索引擎,存储分词和分词位置,不实现对文本分词,ES通过内置分词器,分词的功能交给分词器去完成,常用的分词器有:
Standard Analyzer :按词切分,将词小写
Simple Analyzer:按非字母过滤(符号被过滤掉),将词小写
WhitespaceAnalyzer:按照空格切分,不转小写

ps:Elasticsearch是老外写的,内置的分词器都是英文类的,而我们用户搜索的时候往往搜的是中文,现在中文分词器用得最多的就是IK分词器。

至于分词器内部是如何使用算法实现对人类语言的文本分词的,这个不谈论。
需要知道的是,搜索引擎仅仅是索引库,进存储分词和分词位置,不是具体做分词的,分词器才是,ES和Solr都是这样。

Elasticsearch分词器主要由三部分组成:
Character Filters(文本过滤器,去除HTML)
Tokenizer(切分规则,如:按词切分、按非字母过滤、按空格切分)
TokenFilter(将切分后的词进行处理,如转成小写 or 不转为小写)

3.2 Elasticsearch数据结构(Term Index、Term Dictionary、PostingList)

Elasticsearch的数据结构
不同的数据结构所花的时间往往不一样,你想要查找的时候要快,就需要有底层的数据结构支持:
第一,链表的查找时间复杂度一般是O(n)
第二,接上面,树型的查找时间复杂度一般是O(logn),比链表好些
第三,接上面,哈希表的查找时间复杂度一般是O(1),比树好些
至于Elasticsearch的模糊查询速度很快,Elasticsearch底层数据结构为:Term Index、Term Dictionary、PostingList

Elasticsearch数据结构(Term Index、Term Dictionary、PostingList)
如图:
在这里插入图片描述
解释上图中三个概念(Term Index、Term Dictionary、PostingList)
Term Dictionary定义:存放分词,我们输入一段文字,Elasticsearch会根据分词器对我们的那段文字进行分词(也就是图上所看到的Ada/Allen/Sara…),这些分词汇总起来我们叫做Term Dictionary,在Term Dictionary中的词由于是非常非常多的,所以我们会为其进行排序,等要查找的时候就可以通过二分来查,不需要遍历整个Term Dictionary
PostingList定义:存放分词位置,而我们需要通过分词找到对应的记录,这些文档ID保存在PostingList
Term Index定义:存放分词前缀,放在内存中,由于Term Dictionary的词实在太多了,不可能把Term Dictionary所有的词都放在内存中,于是Elasticsearch还抽了一层叫做Term Index,这层只存储 部分 词的前缀,Term Index会存在内存中(检索会特别快)

3.3 Term Index的优化 + Term Dictionary的优化 + PostingList的优化

3.3.1 Term Index的优化

Term Index的优化:使用FST的存储形式
Term Index在内存中是以FST(Finite State Transducers)的形式保存的,其特点是非常节省内存。FST有两个优点:
1)空间占用小。通过对词典中单词前缀和后缀的重复利用,压缩了存储空间;
2)查询速度快。O(len(str))的查询时间复杂度。

Term Index的优化(三个)
第一,Term Index是存储在内存中的;
第二,Term Index使用FST(Finite State Transducers)的形式保存(节省内存空间)。
第三,Term Dictionary也是为Term Index进行排序(查找的时候方便)

金手指:对于Term Index,为了保证足够快,Term Index存储在内存中,既然存放在内存中,就好缩小占用空间大小,所以,对于Term Index的优化主要是,使用FST存储形式,通过对词典中单词前缀和后缀的重复利用,压缩了存储空间,第二个优化就是使用FST存储形式,O(len(str))的查询时间复杂度

3.3.2 Term Dictionary的优化

金手指:Term Dictionary的优化主要体现在查找性能上,就是使用二分查找。

3.3.3 Term Index的优化

PostingList的优化:使用FOR编码技术对里面的数据压缩 + 使用Roaring Bitmaps来对文档ID进行交并集操作
使用FOR编码技术对里面的数据压缩
PostingList会使用Frame Of Reference(FOR)编码技术对里边的数据进行压缩,节约磁盘空间。
在这里插入图片描述
对于上图的解释:
PostingList使用Roaring Bitmaps来对文档ID进行交并集操作
PostingList里边存的是文档ID,我们查的时候往往需要对这些文档ID做交集和并集的操作(比如在多条件查询时),PostingList使用Roaring Bitmaps来对文档ID进行交并集操作,使用Roaring Bitmaps的好处就是可以节省空间和快速得出交并集的结果。
在这里插入图片描述
对于上图的解释:
Step1:每个数字除去65536,记录结果,每个数字对65536取模,记录结果
1000 变为 (0,1000) 表示1000这个数字除以65536结果为0,取模结果为1000
62101 变为 (0,62101) 表示62101这个数字除以65526结果为0,取模结果为65536,
以此类推,经过Step1,6个数字变为6个(key,value)键值对,并没有节约存储大小,且看Step2
Step2:对于上面6个键值对,根据 除以65536结果 的结果拆分,所以,
1000 和 62101 为一组,它们除以65526结果为0,存储取模65536,所以,存储1000 和 62101
131385、132052、191173 为一组,它们除以65526结果为2,存储取模65536,所以,存储313 980 60101
196658 为一组,它们除以65526结果为3,存储取模65536,所以,存储50
从存储结果来看,存储变小了
Step3:用尽可能小的数据类型存储下来

金手指:Posting List是用来存放Term Dictionary的位置,优化包括两个:
第一,既然是存放Term Dictionary位置,就要知道,怎样使占用的空间尽量小,使用FOR编码技术对里面的数据压缩;
第二,既然是存放Term Dictionary位置,就要能够快速返回ID范围,PostingList使用Roaring Bitmaps来对文档ID进行交并集操作

3.3.4 小结:搞懂ES数据结构的优化(ES索引库为什么查找快?就是ES数据结构决定的)

ES索引库为什么查找快?就是ES数据结构决定的,对应于ES的数据结构,都有优化方式
Term Index的优化 + Term Dictionary的优化 + PostingList的优化

金手指:对于Term Index,为了保证足够快,Term Index存储在内存中,既然存放在内存中,就好缩小占用空间大小,所以,对于Term Index的优化主要是,使用FST存储形式,通过对词典中单词前缀和后缀的重复利用,压缩了存储空间,第二个优化就是使用FST存储形式,O(len(str))的查询时间复杂度

金手指:Term Dictionary的优化主要体现在查找性能上,就是使用二分查找。

金手指:Posting List是用来存放Term Dictionary的位置,优化包括两个:
第一,既然是存放Term Dictionary位置,就要知道,怎样使占用的空间尽量小,使用FOR编码技术对里面的数据压缩;
第二,既然是存放Term Dictionary位置,就要能够快速返回ID范围,PostingList使用Roaring Bitmaps来对文档ID进行交并集操作

3.4 Elasticsearch的概念和架构(术语和架构是联系在一起的)

3.4.1 ES中的概念

Elasticsearch中的概念
Index:Elasticsearch的Index相当于数据库的Table
Type:这个在新的Elasticsearch版本已经废除(在以前的Elasticsearch版本,一个Index下支持多个Type–有点类似于消息队列一个topic下多个group的概念)
Document:Document相当于数据库的一行记录
Field:相当于数据库的Column的概念
Mapping:相当于数据库的Schema的概念
DSL:相当于数据库的SQL(给我们读取Elasticsearch数据的API)
在这里插入图片描述

3.4.2 ES架构

ES架构:Elasticsearch是分布式存储的
第一,分布式节点(Master+Slave):一个Elasticsearch集群会有多个Elasticsearch节点,所谓节点实际上就是运行着Elasticsearch进程的机器。在众多的节点中,其中会有一个Master Node,它主要负责维护索引元数据、负责切换主分片和副本分片身份等工作(后面会讲到分片的概念),如果主节点挂了,会选举出一个新的主节点。
第二,分片:Elasticsearch最外层的是Index(相当于数据库 表的概念);一个Index的数据我们可以分发到不同的Node上进行存储,这个操作就叫做分片。比如现在集群里边有4个节点,我现在有一个Index,想将这个Index在4个节点上存储,那我们可以设置为4个分片。这4个分片的数据合起来就是Index的数据
第三,分片存储原因
(1)如果一个Index的数据量太大,只有一个分片,那只会在一个节点上存储,随着数据量的增长,一个节点未必能把一个Index存储下来。
(2)多个分片,在写入或查询的时候就可以并行操作(从各个节点中读写数据,提高吞吐量)
第四,分片实现高可用:如果某个节点挂了,那部分数据就丢了吗?
在Elasticsearch中,分片会有主分片和副本分片之分(为了实现高可用)数据写入的时候是写到主分片,副本分片会复制主分片的数据,读取的时候主分片和副本分片都可以读。
Index需要分为多少个主分片和副本分片都是可以通过配置设置的
如果某个节点挂了,前面所提高的Master Node就会把对应的副本分片提拔为主分片,这样即便节点挂了,数据就不会丢。

四、ES读写

4.1 Elasticsearch 写入的流程(主要是ES的内部结构)

Elasticsearch 写入的流程
上面我们已经知道当我们向Elasticsearch写入数据的时候,是写到主分片上的,我们可以了解更多的细节。
客户端写入一条数据,到Elasticsearch集群里边就是由节点来处理这次请求:
在这里插入图片描述
集群上的每个节点都是coordinating node(协调节点),协调节点表明这个节点可以做路由。比如节点1接收到了请求,但发现这个请求的数据应该是由节点2处理(因为主分片在节点2上),所以会把请求转发到节点2上。

coodinate(协调)节点通过hash算法可以计算出是在哪个主分片上,然后路由到对应的节点
shard = hash(document_id) % (num_of_primary_shards)

路由到对应的节点以及对应的主分片时,会做以下的事(文字描述对应图片):
(1)将数据写到内存缓存区
(2)然后将数据写到translog缓存区
(3)每隔1s数据从buffer中refresh到FileSystemCache中,生成segment文件,一旦生成segment文件,就能通过索引查询到了
(4)refresh完,memory buffer就清空了。
(5)每隔5s中,translog 从buffer flush到磁盘中
(6)定期/定量从FileSystemCache中,结合translog内容flush index到磁盘中。
在这里插入图片描述
对于上图的解释:
(1)Elasticsearch会把数据先写入内存缓冲区,然后每隔1s刷新到文件系统缓存区(当数据被刷新到文件系统缓冲区以后,数据才可以被检索到)。所以:Elasticsearch写入的数据需要1s才能查询到
(2)为了防止节点宕机,内存中的数据丢失,Elasticsearch会另写一份数据到日志文件上,但最开始的还是写到内存缓冲区,每隔5s才会将缓冲区的刷到磁盘中。所以:Elasticsearch某个节点如果挂了,可能会造成有5s的数据丢失。
(3)等到磁盘上的translog文件大到一定程度或者超过了30分钟,会触发commit操作,将内存中的segement文件异步刷到磁盘中,完成持久化操作。
小结:写内存缓冲区(定时去生成segement,生成translog),能够让数据能被索引、被持久化。最后通过commit完成一次的持久化。等主分片写完了以后,会将数据并行发送到副本集节点上,等到所有的节点写入成功就返回ack给协调节点,协调节点返回ack给客户端,完成一次的写入。

4.2 Elasticsearch更新和删除(写操作)

Elasticsearch更新和删除
Elasticsearch的更新和删除操作流程:

第一步:给对应的doc记录打上.del标识;
第二步:如果是删除操作就打上delete状态;
第三步:如果是更新操作就把原来的doc标志为delete,然后重新新写入一条数据(更新=先删除再插入);
相同点:更新和删除的时候,实际上都是把doc状态标记为delete状态

对于ES,前面提到,每隔1s会生成一个segement 文件,那segement文件会越来越多越来越多。Elasticsearch会有一个merge任务,会将多个segement文件合并成一个segement文件。在merge合并的过程中,会把带有delete状态的doc给物理删除掉,就完成了物理删除。

4.3 Elasticsearch的两种查询方式(读操作:Get、Query/Search)

Elasticsearch查询
查询我们最简单的方式可以分为两种:
(1)根据ID查询doc;代码:public Document doc(int docID);
(2)根据query(搜索词)去查询匹配的doc;代码:public TopDocs search(Query query, int n); public Document doc(int docID);

第一种查询Get:根据ID去查询具体的doc的流程是:
第一步,检索内存的Translog文件;
第二步,检索硬盘的Translog文件;
第三步,检索硬盘的Segement文件。
第二种查询Search/Query:根据query去匹配doc的流程是:
同时去查询内存和硬盘的Segement文件
一图小结两种查询
在这里插入图片描述

两种查询方式:Get(通过ID去查Doc是实时的),Query/Search(通过query去匹配Doc是近实时的),
问题:为什么Get是实时的,Query/Search是近实时的?
理由:因为segement文件是每隔一秒才生成一次的。

Elasticsearch查询又分可以为三个阶段:

第一阶段:QUERY_AND_FETCH(查询完就返回整个Doc内容)
第二阶段:QUERY_THEN_FETCH(先查询出对应的Doc id ,然后再根据Doc id 匹配去对应的文档)
第三阶段:DFS_QUERY_THEN_FETCH(先算分,再查询)

「这里的 “分” 指的是 词频率和文档的频率(Term Frequency、Document Frequency)众所周知,出现频率越高,相关性就更强」
在这里插入图片描述
ps:一般我们用得最多的就是QUERY_THEN_FETCH,第一种查询完就返回整个Doc内容(QUERY_AND_FETCH)只适合于只需要查一个分片的请求。

QUERY_THEN_FETCH总体的流程流程大概是

(1)客户端请求发送到集群的某个节点上。集群上的每个节点都是coordinate node(协调节点)
(2)然后协调节点将搜索的请求转发到所有分片上(主分片和副本分片都行)
(3)每个分片将自己搜索出的结果(doc id)返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,产出最终结果。
(4)接着由协调节点根据 doc id 去各个节点上拉取实际的 document 数据,最终返回给客户端。

Query Phase阶段时节点做的事

(1)协调节点向目标分片发送查询的命令(转发请求到主分片或者副本分片上)
(2)数据节点(在每个分片内做过滤、排序等等操作),返回doc id给协调节点

Fetch Phase阶段时节点做的事

(1)协调节点得到数据节点返回的doc id,对这些doc id做聚合,然后将目标数据分片发送抓取命令(希望拿到整个Doc记录)
(2)数据节点按协调节点发送的doc id,拉取实际需要的数据返回给协调节点

小结:ES内部主流程:由于Elasticsearch是分布式的,所以需要从各个节点都拉取对应的数据,然后最终统一合成给客户端(ps:只是Elasticsearch把这些活都干了,我们在使用的时候无感知而已)

五、面试金手指

略,全文都是要记的。

梳理一下全文,ES两个作用,第一,在ELK组合收集日志中对日志搜索,第二,使用ES对特定的业务数据搜索

第一,ElasticSearch定义 + ElasticSearch取代数据库like查找的现实需求(三个理由)

第二,为什么Elasticsearch为什么可以实现快速的“模糊匹配”或者快速的“相关性查询”? 标准答案:倒排索引 + 分词器

第三,Elasticsearch数据结构(Term Index、Term Dictionary、PostingList)+ 数据结构内部的优化 (TermIndex的优化 + PostingList的优化)
3.1 ES索引库为什么查找快?就是ES数据结构决定的,对应于ES的数据结构,都有优化方式
Term Index的优化 + Term Dictionary的优化 + PostingList的优化
3.2 金手指:对于Term Index,为了保证足够快,Term Index存储在内存中,既然存放在内存中,就好缩小占用空间大小,所以,对于Term Index的优化主要是,使用FST存储形式,通过对词典中单词前缀和后缀的重复利用,压缩了存储空间,第二个优化就是使用FST存储形式,O(len(str))的查询时间复杂度
3.3 金手指:Term Dictionary的优化主要体现在查找性能上,就是使用二分查找。
3.4 金手指:Posting List是用来存放Term Dictionary的位置,优化包括两个:

3.4.1 既然是存放Term Dictionary位置,就要知道,怎样使占用的空间尽量小,使用FOR编码技术对里面的数据压缩;
3.4.2 既然是存放Term Dictionary位置,就要能够快速返回ID范围,PostingList使用Roaring Bitmaps来对文档ID进行交并集操作

第四,Elasticsearch的概念和架构(术语和架构是联系在一起的)

第五,ES内部结构 + ES读写流程(读就是查询,包括两种方式;写包括更新和删除)
5.1 Elasticsearch 写入的流程(主要是ES的内部结构)
5.2 Elasticsearch更新和删除(写操作)
5.3 Elasticsearch的两种查询方式(读操作:Get、Query/Search)

六、小结

原理层面:Elasticsearch,完成了。

天天打码,天天进步!!!

猜你喜欢

转载自blog.csdn.net/qq_36963950/article/details/108952827