前言

        在大数据时代,Elasticsearch除了是一款分布式搜索引擎,还是一款大数据近实时分析引擎。那么 你知道elasticsearch的一次搜索过程到底是怎样的吗?不要着急我们先来看看elasticsearch是如何存储的(倒排索引),以及elasticsearch是如何进行分词生成倒排索引的。

   一、倒排索引        

     谈到es肯定要讲倒排索引 ,我们都知道倒排索引是搜索引擎中一种非常重要的数据结构。有倒排索引就肯定有正排索引。这里我们用一本书目录来解释下正排索引和倒排索引的区别。 

1.1正排索引就好比一本书(mysql高性能)的目录 :

es语句是如何执行的                 

比如我对读写锁的内容比较关心,那么我就直接打开目录,看到读写锁的相关内容在第4页,我们通过目录(正排索引) 可以快速查到你感兴趣的内容。  但是假设我们这里有个新的需求——需要对mysql高性能这本书中所有的锁做出一个总结。那么我们需要把这本书中所有出现锁这个单词的上下文都阅读一遍,假如有个数据结构刚好记录了每个单词,及每个单词在上下文中出现的位置,那该多好啊。我们就不必从书的第一页翻到最后一页一一找出哪里有锁这个单词,这就是倒排索引。下图我们给出一个简单的示例右边的结构就是一个倒排索引,记录了每个单词出现的次数和在每个文档中出现位置:

 es语句是如何执行的elasticsearch倒排索引的核心组成

1单词词典,记录所有文档的单词,记录单词到倒排列表的关联关系,单词词典一帮通过B+树或者Hash拉链实现满足高性能的插入与查询。

2倒排列表——记录了单词对应的文档集合,由 倒排索引项组成。

3倒排索引项

     文档ID

     词频TF-单词在文档中出现的次数,用于相关性评分。

     位置-单词在文档中分词的位置。用于语句搜索

     偏移-记录单词的开始位置和结束位置用于高亮显示。

我们看一下例子:

es语句是如何执行的

二、Elasticsearch是如何分词的

 文本分析是把文本翻译转换成一系列单词的过程。文本分析又叫做Analysis,一般是通过Analyzer(分析器)来实现的。elasticsearch本身就有很多自带的分词器,当然你自己也可以去定制特定的分词器。分词器的 就是按照一定的规则进行分词。比如把Hello World 分为 Hello 和World两个单词。Analyzer可以分为三个步奏:Character Filters,Tokenizer,Token Filters..

Character Filters 争对原始文本进行一些处理,比如去掉html标签。

Tokenizer 按照规则将文档拆分为单词

Token Filters 将切分完的单词进行加工,比如小写删除stopworlds 增加同意词

比如有一个语句</h>Mastering Elasticsearh</h> Hello World,经过去除html(Character Filters),按照空格进行分词(Tokenizer),大写转小写(Token Filters)是如何执行的

1Mastering Elasticsearh Hello World

2分词为 Mastering,Elasticsearch,Hello,World

3 最后为mastering,elasticsearch,hello,world

我们可以通过 Get /_analyze

{

"analyzer":"standard",

"text": "Mastering Elasticsearh, Hello World"

}

来查看指定的分词器是否能达到效果。

三、ElasticSearch节点类型

节点就是一个启动的elasticsearch实例,本质上就是一个java进程,在生产环境中一台机器上只运行一个Elasticsearch实例。节点的名字可以更具node.name来指定。每个节点启动后数据会保存在data目录下。

节点可以分为Master/Master eligible/Data/Ingest/Coordinating/Machine Learning节点。在这里只介绍Master,Data,Coordinating节点。

Master-eligible nodes和Master Node,每个节点启动后默认就是一个Master eligible节点,可以设置node.master:false禁止。Master-eligible节点可以参加选主流程,成为Master节点。当一个节点启动的时候,它会将自己选举成Master节点。虽然每个节点上都保存了集群的状态,但只有Master节点才能修改集群的状态信息。集群状态信息主要有:所有的节点信息,所有索引和其相关的Mapping与Setting信息,分片的路由信息。

 

Coordinating Node:处理请求的节点,会将请求路由到正确的节点,最终把结果汇聚到一起。比如有一个创建索引的请求,就会路由到Master节点。

Data Node:可以保存数据的节点,叫做Data Node,负责保存分片数据在数据扩展上起到至关重要的作用。

四、一条elasticsearch语句时如何执行的

在生产环境中一个elasticsearch索引往往分布在多个分片上,而文档是存储在分片上的。那么文档到分片之间必定有一个映射关系。实现文档到分片映射的思路有三种模式:

1随机/Round Robin。因为是随机的无法通过算法知道一个文档到底在那个分片上,在分片数特别多的时候需要查询多次才能查到文档。

2维护文档到分片的映射关系。当文档特别多时,维护成本是特别大的。

3实时计算,比如通过hash算法算出到底去那个文档。比如:

shard = hash(_routing)%number_of_primary_shards,一般_routing值是文档的id,当然也可以自定义routing值。

我们来看看更新一个文档的流程:

1用户发送一个更新请求到一个节点,这个节点扮演一个coordinated的角色。

2通过hash算法算出这个路由应该被放到哪一个分片,并把这个请求发送到这个分片上。

3es更新索引是先删除再创建,成功后把成功消息返回给coordinated节点。

4coordinate节点返回消息给用户。

大家可以自己想想删除文档的流程基本和更新一样。

讲完更新逻辑我们来看看是如何创建文档的。

在讲创建文档的流程时首先 我们来看看分片的内部原理,大家都知道一个分片就是ES中的最小工作单元,是一个Lucene的Index,我们可以带着以下问题去学习分片的内部原理

1为什么es是近实时的

2es是如何保证断电时数据不丢失的。

3为什么删除文档,并不会立即释放空间。、

我们需要知道倒排索引采用的是不可变设计模式,为什么一次修改操作对应的是先删除再新增。如果倒排索引可变是不是要考虑并发问题,维护成本是不是也更大了,如果不可变是不是可以直接用page cache存储。我们来看看不可变性的好处:

1无需考虑并发写问题,避免锁机制带来的性能问题。

2一旦读入内核的文件系统缓存我可以一直留在那里,只要vfs有足够的空间,大多数请求可以命中page cache而不是磁盘。

3缓存容易生成和维护/数据可以被压缩。

  在一个lucene Index (分片中)单个倒排索引被称为Segement,单个Segment是不可变的。多个Segment汇集到一起就是Lucene Index.在Lucene Index中有一个文件叫做Commit Point用来记录所有的Segment信息。除此之外删除的文档信息保存在.del上。下图是一个Lucene Index的结构:   

es语句是如何执行的

es在新增一条数据的时候并不会真正的写入Segment而是写入Index Buffer的内存空间。隔了一段时间后系统会调用refresh操作,把文档写入Segment中。需要注意的是Refresh并不会写盘调用fsynce。并且默认是1秒发生一次。同时Index Buffer填满时也会调用Refresh

es语句是如何执行的

因为segment写入磁盘很慢为同时了保证数据不丢失在写index Buffer的时候同时还会写Transaction Log(是不是和redolog很像)TransactionLog会有一个落盘操作。每个分片都有一个Log.refresh过程中Transaction Log不会清空.Transaction Log的大小是512Mb

es语句是如何执行的

每隔30分钟es会调用Flush操作,具体的流程如下:

1清空index buffer并且refresh。

2调用fsync将page cache 写入磁盘。

3清空TransactionLog

除了以上操作,es还会定期MergeSegment,Merge的工作主要有合并减少index中的Segments,删除以删除的文档。

看完以上文章估计对查询请求也应该有一个大致的印象了。

 

相关文章: