Elasticsearch 技术分析(八):剖析 Elasticsearch 的索引原理

Elasticsearch 技术分析(八):剖析 Elasticsearch 的索引原理

1|0前言

创建索引的时候,我们通过Mapping 映射定义好索引的基本结构信息,接下来我们肯定需要往 ES 里面新增业务文档数据了,例如用户,日志等业务数据。新增的业务数据,我们根据 Mapping 来生成对应的倒排索引信息 。

我们一直说,Elasticsearch是一个基于Apache Lucene 的开源搜索引擎。Elasticsearch的搜索高效的原因并不是像Redis那样重依赖内存的,而是通过建立特殊的索引数据结构--倒排索引实现的。由于它的使用场景:处理PB级结构化或非结构化数据,数据量大且需要持久化防止断电丢失,所以 Elasticsearch 的数据和索引存储是依赖于服务器的硬盘。这也是为什么我们在ES性能调优的时候可以将使用SSD硬盘存储作为其中一个优化项来考虑。

倒排索引的概念,我相信大家都已经知道了,这里就不在赘述,倒排索引可以说是Elasticsearch搜索高效和支持非结构化数据检索的主要原因了,但是倒排索引被写入磁盘后是不可改变 的:它永远不会修改

2|0段和提交点

倒排索引的不可变性,这点主要是因为 Elasticsearch 的底层是基于 Lucene,而在 Lucene 中提出了按段搜索的概念,将一个索引文件拆分为多个子文件,则每个子文件叫作,每个段都是一个独立的可被搜索的数据集,并且段具有不变性,一旦索引的数据被写入硬盘,就不可再修改。

 的概念提出主要是因为:在早期全文检索中为整个文档集合建立了一个很大的倒排索引,并将其写入磁盘中。如果索引有更新,就需要重新全量创建一个索引来替换原来的索引。这种方式在数据量很大时效率很低,并且由于创建一次索引的成本很高,所以对数据的更新不能过于频繁,也就不能保证时效性。

而且在底层采用了分段的存储模式,使它在读写时几乎完全避免了锁的出现,大大提升了读写性能。说到这,你们可能会想到 ConcurrentHashMap 的分段锁 的概念,其实原理有点类似。

而且 Elasticsearch 中的倒排索引被设计成不可变的,有以下几个方面优势

不需要锁。如果你从来不更新索引,你就不需要担心多进程同时修改数据的问题。

一旦索引被读入内核的文件系统缓存,便会留在哪里。由于其不变性,只要文件系统缓存中还有足够的空间,那么大部分读请求会直接请求内存,而不会命中磁盘。这提供了很大的性能提升。

其它缓存(像filter缓存),在索引的生命周期内始终有效。它们不需要在每次数据改变时被重建,因为数据不会变化。

写入单个大的倒排索引允许数据被压缩,减少磁盘 I/O 和 需要被缓存到内存的索引的使用量。

每一个本身都是一个倒排索引,但索引在 Lucene 中除表示所有段的集合外,还增加了提交点的概念。

为了提升写的性能,Lucene并没有每新增一条数据就增加一个段,而是采用延迟写的策略,每当有新增的数据时,就将其先写入内存中,然后批量写入磁盘中。若有一个段被写到硬盘,就会生成一个提交点,提交点就是一个列出了所有已知段和记录所有提交后的段信息的文件

3|0写索引的流程

上面说过 ES 的索引的不变性,还有段和提交点的概念。那么它的具体实现细节和写入磁盘的过程是怎样的呢?

用户创建了一个新文档,新文档被写入内存中。

不时地, 缓存被提交,这时缓存中数据会以段的形式被先写入到文件缓存系统而不是直接被刷到磁盘。

这是因为,提交一个新的段到磁盘需要一个fsync 来确保段被物理性地写入磁盘,这样在断电的时候就不会丢失数据。 但是 fsync 操作代价很大;如果每次索引一个文档都去执行一次的话会造成很大的性能问题,但是这里新段会被先写入到文件系统缓存,这一步代价会比较低。

新的段被写入到文件缓存系统,这时内存缓存被清空。在文件缓存系统会存在一个未提交的段。虽然新段未被提交(刷到磁盘),但是文件已经在缓存中了, 此时就可以像其它文件一样被打开和读取了。

到目前为止索引的段还未被刷新到磁盘,如果没有用 fsync 把数据从文件系统缓存刷(flush)到硬盘,我们不能保证数据在断电甚至是程序正常退出之后依然存在。Elasticsearch 增加了一个 translog ,或者叫事务日志,在每一次对 Elasticsearch 进行操作时均进行了日志记录。如上图所示,一个文档被索引之后,就会被添加到内存缓冲区,并且同时追加到了 translog。

每隔一段时间,更多的文档被添加到内存缓冲区和追加到事务日志(translog),之后新段被不断从内存缓存区被写入到文件缓存系统,这时内存缓存被清空,但是事务日志不会。随着 translog 变得越来越大,达到一定程度后索引被刷新,在刷新(flush)之后,段被全量提交,一个提交点被写入硬盘,并且事务日志被清空。

从整个流程我们可以了解到以下几个问题:

为什么说 ES 搜索是近实时的?

因为文档索引在从内存缓存被写入到文件缓存系统时,虽然还没有进行提交未被 flush 到磁盘,但是缓冲区的内容已经被写入一个段(segment6)中且新段可被搜索。这就是为什么我们说 Elasticsearch 是近实时搜索: 文档的变化并不是立即对搜索可见,但会在一秒之内变为可见。

Elasticsearch 是怎样保证更新被持久化在断电时也不丢失数据?

新索引文档被写入到内存缓存时,同时会记录一份到事务日志(translog)中,translog 提供所有还没有被刷到磁盘的操作的一个持久化纪录。当 Elasticsearch 启动的时候, 它会从磁盘中使用最后一个提交点去恢复已知的段,并且会重放 translog 中所有在最后一次提交后发生的变更操作。

translog 也被用来提供实时 CRUD 。当你试着通过ID查询、更新、删除一个文档,它会在尝试从相应的段中检索之前, 首先检查 translog 任何最近的变更。这意味着它总是能够实时地获取到文档的最新版本。

3|1段合并

由于自动刷新流程每秒会创建一个新的段 ,这样会导致短时间内的段数量暴增。而段数目太多会带来较大的麻烦。 每一个段都会消耗文件句柄、内存和cpu运行周期。更重要的是,每个搜索请求都必须轮流检查每个段;所以段越多,搜索也就越慢。

Elasticsearch通过在后台进行段合并来解决这个问题。小的段被合并到大的段,然后这些大的段再被合并到更大的段。

段合并的时候会将那些旧的已删除文档 从文件系统中清除。 被删除的文档(或被更新文档的旧版本)不会被拷贝到新的大段中。

4|0如何更新索引

上文阐述了索引的持久化流程和倒排索引被设定为不可修改以及这样设定的好处。因为它是不可变的,你不能修改它。但是如果你需要让一个新的文档可被搜索,这就涉及到索引的更新了,索引不可被修改但又需要更新,这种看似矛盾的要求,我们需要怎么做呢?

ES 的解决方法就是:用更多的索引。什么意思?就是原来的索引不变,我们对新的文档再创建一个索引。这样说完不知道大家有没有疑惑或者没理解,我们通过图表的方式说明下。

假如我们现有两个日志信息的文档,信息如下:

Doc 1:the request param is name = 'zhang san' and age is 20.

Doc 2:the response result is code = 0000 and msg = 'success'.

这时候我们得到的倒排索引内容(省略一部分)是:

词项(term)文档(Doc)

thedoc 1,doc 2

requestdoc 1

paramdoc 1,doc 2

isdoc 1,doc 2

namedoc 1

responsedoc 2

resultdoc 2

......

如果我们这时新增一个文档 doc 3:the request param is name = 'li si' and sex is femal,或者修改文档 doc 2的内容为:the response result is code = 9999 and msg = 'false'。这时 ES 是如何处理的呢?

正如上文所述的,为了保留索引不变性,ES 会创建一个新的索引,对于新增的文档索引信息如下:

词项(term)文档(Doc)

thedoc 3

requestdoc 3

paramdoc 3

isdoc 3

namedoc 3

sexdoc 3

......

对于修改的文档索引信息如下;

词项(term)文档(Doc)

thedoc 2

responsedoc 2

resultdoc 2

isdoc 2

codedoc 2

sexdoc 2

......

通过增加新的补充索引来反映新近的修改,而不是直接重写整个倒排索引。每一个倒排索引都会被轮流查询到(从最早的开始),查询完后再对结果进行合并。

正如上文所述那样,对于修改的场景来说,同一个文档这时磁盘中同时会有两个索引数据一个是原来的索引,另一个是修改之后的索引。

以正常逻辑来看,我们知道搜索的时候肯定以新的索引为标准,但是段是不可改变的,所以既不能从把文档从旧的段中移除,也不能修改旧的段来进行反映文档的更新。 取而代之的是,每个提交点会包含一个 .del文件,文件中会列出这些被删除文档的段信息。

当一个文档被 “删除” 时,它实际上只是在.del 文件中被 标记 删除。一个被标记删除的文档仍然可以被查询匹配到, 但它会在最终结果被返回前从结果集中移除。

文档更新也是类似的操作方式:当一个文档被更新时,旧版本文档被标记删除,文档的新版本被索引到一个新的段中。 可能两个版本的文档都会被一个查询匹配到,但被删除的那个旧版本文档在结果集返回前就已经被移除。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • 概要 本篇我们来看看shard内部的一些操作原理,了解一下人家是怎么玩的。 倒排索引 倒排索引的结构,是非常适合用...
    清茶豆奶阅读 422评论 0 0
  • 2018-12-04 https://segmentfault.com/a/1190000014480912 选举...
    Ary_zz阅读 675评论 0 0
  • es的索引搜索是以lucene为底层的,但是lucene是没有实现分布式,lucene提供了核心的索引和搜索引擎,...
    Tim在路上阅读 5,392评论 0 14
  • 今天跟先生的妹妹沟通交流了一些对某些事情的看法,有些是一致的,可有些是话不投机半句多! 之前我和她是属于同一个性格...
    别来无恙朱梅阅读 326评论 7 2
  • 1 环境说明 2 安装依赖 ==根据需求安装== 需要特殊处理的依赖 3 下载&安装 官方下载页面 3.1 扩展插...
    嗤呃阅读 2,628评论 0 0