Elasticsearch系列---shard内部原理

概要

本篇咱们来看看shard内部的一些操做原理,了解一下人家是怎么玩的。java

倒排索引

倒排索引的结构,是很是适合用来作搜索的,Elasticsearch会为索引的每一个index为analyzed的字段创建倒排索引。数据库

基本结构

倒排索引包含如下几个部分:缓存

  • 某个关键词的doc list
  • 某个关键词的全部doc的数量IDF(inverse document frequency)
  • 某个关键词在每一个doc中出现的次数:TF(term frequency)
  • 某个关键词在这个doc中的次序
  • 每一个doc的长度:length norm
  • 某个关键词的全部doc的平均长度

记录这些信息,就是为了方便搜索的效率和_score分值的计算。安全

不可变性

倒排索引写入磁盘后就是不可变的,这样有几个好处:服务器

  1. 不须要锁,若是不更新索引,不用担忧锁的问题,能够支持较高的并发能力
  2. 若是cache内存足够,不更新索引的话,索引能够一直保存在os cache中,能够提高IO性能。
  3. 若是数据不变,filter cache会一直驻留在内存。
  4. 索引数据能够压缩,节省cpu和io开销。

doc底层原理

前面提到倒排索引是基于不可变模式设计的,但实际Elasticsearch源源不断地有新数据进来,那光是创建、删除倒排索引,岂不是很是忙?微信

若是真是不停地创建,删除倒排索引,那ES压力也太大了,确定不是这么实现的。ES经过增长新的补充索引来接收新的文档和修改的文档,而不是直接用删除重建的方式重写整个索引。架构

doc写入

整个写入过程以下图所示:并发

  1. 新文档先写入内存索引缓存
  2. 当间隔必定时间(1秒),将缓存的数据进行提交,这个过程会建立一个Commit Point,Commit Point包含index segment的信息。
  3. 缓存的数据写入新的index segment。
  4. index segment的数据先写入os-cache中
  5. 等待操做系统将os-cache的数据强制刷新到磁盘中
  6. 写入磁盘完成后,新的index segment被打开,此时segment内的文档能够被搜索到。
  7. 同时buffer的数据被清空,等待下一次新的文档写入。

index segment翻译过来叫"段",每秒会建立一个,ES把这个1秒内收到的、须要处理的文档都放在这个段里,能够把段认为是倒排索引的一个子集。异步

索引、分片、段的关系以下:
索引包含多个分片,每一个分片是一个Lucene索引实例,一个分片下面有多个段。若是把分片看做是一个独立的倒排索引结构,那么这个倒排索引是由多个段文件的集合。
三者之间是包含关系:索引包含多个分片,分片包含多个段。async

doc删除和更新

当文档被删除时,Commit Point会把信息记录在.del文件中,在.del文件中会标识哪些文档是有deleted标记的,但该文档仍是存在于原先的index segment文件里,一样可以被检索到,只是在最终结果处理时,标记为deleted的文档被会过滤掉。

更新也是相似的操做,更新会把旧版本的文档标记为deleted,新的文档会存储在新的index segment中。

近实时搜索

上面的流程细节的童鞋能够会发现,每次都须要fsync磁盘,数据才是可搜索的,那IO压力将特别大,耗费时间比较长,而且执行周期由操做系统控制,从一个新文档写入到能够被搜索,超过1分钟那是常有的事。

因此Elasticsearch对此作了一个改进:
index segment信息写入到os-cache中,即完成上面的第4步,该segment内的文档信息就能够被搜索到了。fsync操做就不当即执行了,

os-cache的写入代价比较低,最耗时的fsync操做交由操做系统调度执行。

上述的index segment写入到os-cache,并打开搜索的过程,叫作refresh,默认是每隔1秒refresh一次因此,es是近实时的,数据写入到能够被搜索,默认是1秒。

refresh的时间也能够设置,好比咱们一些日志系统,数据量特别大,但实时性要求不高,咱们为了优化资源分配,就能够把refresh设置得大一些:

PUT /music
{
  "settings": {
    "refresh_interval": "30s" 
  }
}

此参数须要在建立索引时使用,要注意一下的是除非有充分的依据,才会对refresh进行设置,通常使用默认的便可。

translog机制

上述的写入流程当中,若是fsync到磁盘的操做没执行完成,服务器断电宕机了,可能会致使Elasticsearch数据丢失。Elasticsearch也设计了translog机制,跟关系型数据库的事务日志机制很是像,整个写入过程将变成这样:

  1. 新文档写入内存buffer的同时,也写一份到translog当中。
  2. 内存buffer的数据每隔1秒写入到index segment,并写入os-cache,完成refresh操做。
  3. 内存buffer被清空,但translog一直累加。
  4. 每隔5秒translog信息fsync到磁盘上。
  5. 默认每30分钟或translog累积到512MB时,执行全量commit操做,os-cache中的segment信息和translog信息fsync到磁盘中,持久化完成。
  6. 生成新的translog,旧的translog归档(6.x版本translog作归档操做,不删除)。

flush API

这个执行一个提交而且归档translog的行为称做一次flush。分片每30分钟被自动刷新(flush),或者在 translog 太大的时候(默认512MB)也会刷新,固然也能够手动触发flush的执行,以下请求:

POST /music/_flush

但任其自动flush就够了。若是重启节点前担忧会对索引形成影响,能够手动flush一下。毕竟节点重启后须要从translog里恢复数据,translog越小,恢复就越快。

durability同步和异步

translog写磁盘行为主要有两种,是由index.translog.durability配置项决定的:

  • request:同步写磁盘,每次写请求完成以后当即执行(新增、删除、更新文档),以及primary shard和replica shard同步都会触发,数据安全有保障,不丢失,但会带来一些性能损失。若是是bulk数据导入,每一个文档平摊下来的损失是比较小的。
  • async:异步写磁盘,默认5秒fsync一次,若是有宕机事件,可能会丢失几秒的数据,适用于容许偶尔有数据丢失的场景,如日志系统。

若是系统不接受数据丢失,用translog同步方式,示例设置:

# 异步方式
PUT /music_new
{
  "settings": {
    "index.translog.durability": "async",
    "index.translog.sync_interval": "5s"
  }
}

# 同步方式
PUT /music_new
{
  "settings": {
    "index.translog.durability": "request"
  }
}

segment合并

Elasticsearch针对活跃的索引,每秒都会生成一个新的index segment,这些segment最终会以文件的形式存储在磁盘里,若是不对其进行处理,那么索引运用一段时间后,会有特别多的文件,零碎的文件太多了,也不是什么好事情,更耗费更多的文件资源,句柄等,搜索过程也会变慢。

合并过程

Elasticsearch会在后台对segment进行合并,减小文件的数量,同时,标记为deleted的文档在合并时会被丢弃(delete请求只是将文档标记为deleted状态,真正的物理删除是在段合并的过程当中),合并过程不须要人工干预,让Elasticsearch自行完成便可。

两个已经提交的段和一个未提交的段合并成为一个大的段文件

合并时会挑一些大小接近的段,合并到更大的段中,段合并过程不阻塞索引和搜索。

合并完成后,新的更大的段flush到磁盘中,并完成refresh操做,老的段被删除掉。

optimize API

optimize命令能够强制合并API,并指定最终段的数量,以下命令:

POST /music_new/optimize
{
  "max_num_segments": 1
}

指定segment最大数量为1,表示该索引最终只有一个segment文件。

适用场景
  1. 正常活跃的、常常有更新的索引不建议使用
  2. 日志类的索引,对老数据进行优化时,能够将每一个分片的段进行合并
使用建议
  1. 通常不须要人工干预合并过程
  2. optimize操做会消耗大量的IO资源,使用要慎重考虑

小结

本篇主要介绍shard内部的原理,包含写入、更新删除,translog机制,segment合并等,了解数据库的童鞋对translog机制应该很是熟悉,原理上大同小异,仅做抛砖引玉,谢谢。

专一Java高并发、分布式架构,更多技术干货分享与心得,请关注公众号:Java架构社区
能够扫左边二维码添加好友,邀请你加入Java架构社区微信群共同探讨技术
Java架构社区.jpg

相关文章
相关标签/搜索