2020年5月

我的个人博客:逐步前行STEP

1、elasticsearch 如何使文档可以被搜索

为了支持全文检索而采用倒排索引,倒排索引包含一个有序列表,列表包含所有文档出现过的词项 ,对于每一个词项,包含了它所有曾出现过文档的列表。
早期的倒排索引,会在文档变化时,重建新的索引,直到完成后替换掉旧的索引,这样新的变化就可以被搜索到。
倒排索引具有不变性,这使得索引一旦被读入文件系统缓存,便会留在那里,只要有足够的空间,就可以使大部分请求直接命中缓存,很大地提升了性能,缺点是,如果有新的文档需要被搜索,只能重建整个索引。

2、如何使用更好的方式实现倒排索引的更新

上面说了,由于索引的不变性,更新索引的方式是重建整个索引,这种方式对于数据量和更新频率的情况是有很大限制的,所以elasticsearch通过增加新的补充索引来反映新近的修改,而不是直接重写整个倒排索引。最终通过轮询每一个倒排索引并对结果进行合并才创建新的索引。

  • 按段搜索
  1. 基于lucene 引入了 按段搜索的概念,每一个段,都是一个倒排索引,而一个Lucene索引,除了包含多个段之外,还包含commit point、.liv文件。

其中,commit point记录了每一次的创建新段,当一个段存在文档被删除,会维护该信息在 .liv 文件里面。

逐段搜索会以如下流程进行工作:

  1. 新增的文档写入内存缓存
  2. 每隔一段时间,创建一个新段,将文档从内存缓存中被提交到新段
  3. 新段写入到磁盘(文件系统缓存)
  4. 新的commit point 也 写入到磁盘(文件系统缓存)
  5. 磁盘 fsync ,从文件系统缓存中价格写入操作同步到物理磁盘
  6. 这个新的段被开启, 使得段内文档对搜索可见
  7. 清空内存缓存,继续接受新的文档

3、如何实现近实时搜索

上面的写入到可检索过程,主要瓶颈在磁盘fync,大量文档需要写入磁盘可能造成很明显的延迟,而且每次的索引一个文档都要做磁盘fsync会造成性能问题。所以elasticsearch更轻量的方式来使一个文档可被搜索——将fsync从【写入到可检索】的步骤中移除——在新段写入文件系统缓存之后就可检索。
在第1部分中描述了倒排索引在文件系统缓存中被请求,所以在新段写入文件系统缓存之后就可以被打开和读取了。写入文件系统缓存和打开新段被称为refresh,默认情况下每秒进行一次refresh,也可以更改该项设置,或者手动执行。
有了refresh之后,索引文档的步骤更改为:

  1. 新增的文档写入内存缓存
  2. 每隔一段时间,创建一个新段,refresh,此时新段可被检索
  3. 通过fsync刷新磁盘(flush) ,从文件系统缓存中将所有写入操作同步到物理磁盘
  4. 清空内存缓存,继续接受新的文档

4、数据持久化

上面说到新段先写入文件系统缓存,再fsync到磁盘,在这个过程中,如果断电,则无法将未来得及同步的数据持久化到磁盘,所以elasticsearch 增加了一个 translog,也叫事务日志,记录了每一次的操作。

  1. 在文档写入内存缓存的同时,写入到translog中
  2. 执行refresh时,会清空内存缓存,且没有fsync到磁盘时,translog仍然保留
  3. 每隔一段时间或者translog文件太大,索引被刷新,所有在内存缓冲区的文档都被写入一个新的段,清空内存缓存,一个提交点被写入硬盘,新的traslog文件被创建,旧的被删除

而在执行refresh后,fsync之前,如果重启elasticsearch,它会从磁盘中使用最后一个提交点去恢复已知的段,并且会重放 translog 中所有在最后一次提交后发生的变更操作。

5、段合并

由于在自动刷新流程每秒会创建一个新的段,段时间内就会积累大量的段,每一个段都会消耗文件句柄、内存和cpu运行周期,而且在检索时需要遍历所有段来查找文档,这样无疑是一个巨大的性能瓶颈。elasticsearch通过在后台进行段合并来解决这个问题,小的段被合并到大的段,然后这些大的段再被合并到更大的段,段合并的时候会将那些旧的已删除文档从文件系统中清除。
段合并不会中断检索和索引的过程,在索引的时候,创建新段,段合并就会自动执行,合并的段将继续执行之后的步骤。
手动进行强制段合并可使用optimize api,它会将一个分片强制合并到 max_num_segments 个段,但该操作可能会无限制的使用资源,造成集群内短时间内无法响应。