参考:
ES官方文档分片内部原理:https://www.elastic.co/guide/cn/elasticsearch/guide/current/inside-a-shard.html
索引的解释:
名词的索引:指的是由多个分片组成的集合,类似于数据库中的数据库名称
动词的索引:es文档存储的过程
1.倒排索引
为了快速的全文检索,elasticsearch使用了倒排索引的结构,使用分析器将每篇文档的字段中的内容分词(每一个词称为term或tokens),创建了一个包含所有不重复词条的列表,从列表中可以找到某个词条出现过的文档,假如有两个文档:
- quick brown
- Quick brown foxes
倒排索引结构如下(实际的倒排索引存储的信息更多,这里只是简单举例), X代表在哪个文档出现过:
Term doc1 doc2
quick X
brown X X
foxes X
Quick X
假如搜索quick brown,可以从倒排索引结构中看到quick在文档1出现过,brown在文档1和文档2都出现过,因此将文档1和文档2返回即可。
想深入了解的可参考:时间序列数据库的秘密(2)——索引
2.倒排索引的不变性
倒排索引写入磁盘后是不可改变的,也就是不能修改它,既然不可修改,那么新增文档的时候就需要重新建索引,但是如果每次新增文档就重建索引,这样的性能消耗是很大的,接下来看一下elasticsearch是如何解决这个问题的。
倒排索引不变性的优点可参考官方文档:
https://www.elastic.co/guide/cn/elasticsearch/guide/current/making-text-searchable.html
3.动态更新索引
首先介绍一下Lucene中的Segment(段):
一个Lucene索引(注意是Lucene索引,不是elasticsearch索引)由多个段和提交点组成,段可以看做是一个倒排索引,提交点记录了所有可用的段,通过提交点就可以获取所有可用的段,然后在段上做查询。
如下图所示,一个Lucene索引,包含了三个段和一个提交点,通过提交点,使三个段变得可被查询:
Lucene索引和elasticsearch索引的区别:
Lucene索引在elasticsearch 称为分片,在elasticsearch中一般会为一个索引建多个分片,elasticsearch的索引是由多个分片组成的,那么es索引和Lucene索引的关系如下:
知道了段的概念,就可以理解elasticsearch如何保留倒排索引不变的情况下实现倒排索引的更新,它是通过增加新的段来更新索引,而不是对整个倒排索引重写。
流程:
(1)新增的文档首先进入到内存缓存中
(2)默认的情况下,1s刷新一次,将内存中的文档写入段中,这个过程称为refresh
(3)段被写入文件系统缓存中,es会定时的将段写入磁盘中,同时生成新的提交点,记录所有可用的段,这个过程称为flush
(4)新的段被开启,让它包含的文档可以被搜索
(5)内存缓存被清空,准备接收新的文档
4.文档的删除和更新
由于段是不可变的,既然插入的时候是通过新增段来实现,那么删除和更新的时候是如何做的?
提交点除了记录可用的段之外,还包含了一个.del文件,文件中列出被删除文档的段信息,当一个文档被删除时,实际上只是在.del文件中被记录了下来,它还可以被搜索到,只是在返回结果前将该文档从结果集中移除了。文档更新也是类似的方式。
.del中记录的文档将在段合并的时候被清除。
5.近实时搜索
由于将段写入磁盘中的代价比较高,但是段被写入文件系统缓存的代价比较低,如果利用这个特性,当段被写入文件系统缓存时就变得可搜索,就能解决写入磁盘的性能问题,减少文档索引到可被搜索的时间,更接近实时搜索。
新增文档进入内存缓存区:
内存缓存区中的文档被refresh到段中,如下图所示,此时新的段虽然未被写入磁盘,但是此时已经可以打开,变的可被搜索,这就是elasticsearch近实时搜索的原理,文档的变化不是立即对搜索可见,但是会在一秒之内(refresh默认1s刷新一次)变得可见:
6.段合并
由于refresh将文档写入段中,会导致段的数量不断增多,而每一个段都会消耗文件句柄、内存和cpu运行周期,每个搜索请求也会轮询每个段查找是否包含需要的文档,段数量的增多将会导致搜索时间的加长。
elsticsearch通过在后台使用段合并来解决这个问题,合并进程会选择一部分大小相似的段将它们合并到更大的段中而不会影响搜索。
在段合并的过程中也会将.del文件中记录的文档删除,从而实现真正意义上的删除。
下图将两个已提交的段和一个未提交的段合并到了一个更大的段:
合并结束后,老的段被删除,新的段被flush到磁盘,提交点内记录了新的段,排除旧的段:
需要注意的是段合并需要消耗大量的I/O和CPU资源,因此elasticsearch在默认情况下对合并流程进行了资源限制。也可以通过optimize
API强制合并段。