本文主要是介绍elasticsearch内部原理自我总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
环境
elasticsearch:5.5
自我理解
对于elasticsearch
我自己也是新手,只是想做点笔记,记录下自己的理解。
我一直想明白当一个文档被索引进elasticsearch
时,其内部干什么了?
本人结合网上的教程,记录下
倒排索引
一个新文档要使其可见,需要使用倒排索引
,其长相如下:
词项 | 文档1 | 文档2 | 文档3 | … |
---|---|---|---|---|
中国 | X | X | … | |
小米 | X | X | X | … |
华为 | X | X | … | |
南昌 | X | X | … |
有了倒排索引之后,新进的文档就可以被搜索到了。
注意:倒排索引被写入磁盘后是 不可改变 的:它永远不会修改。 不变性有重要的价值
①不需要锁。如果你从来不更新索引,你就不需要担心多进程同时修改数据的问题。
② 一旦索引被读入内核的文件系统缓存,便会留在哪里,由于其不变性。只要文件系统缓存中还有足够的空间,那么大部分读请求会直接请求内存,而不会命中磁盘。这提供了很大的性能提升。
③其它缓存(像filter缓存),在索引的生命周期内始终有效。它们不需要在每次数据改变时被重建,因为数据不会变化。
④写入单个大的倒排索引允许数据被压缩,减少磁盘 I/O 和 需要被缓存到内存的索引的使用量。
当然,一个不变的索引也有不好的地方。主要事实是它是不可变的! 你不能修改它。如果你需要让一个新的文档 可被搜索,你需要重建整个索引。这要么对一个索引所能包含的数据量造成了很大的限制,要么对索引可被更新的频率造成了很大的限制。
动态更新(倒排)索引
由于倒排索引具有不变性(一旦被写入磁盘就不能再改变),那么我们如何来实现更新倒排索引呢?官方的做法就是用更多的倒排索引。通过增加新的倒排索引来反应新近的修改,而不是重写整个倒排索引,毕竟重写整个倒排索引,要消耗巨大资源;每一个倒排索引都会被轮流查询到–从最早的开始–查询完后再对结果进行合并。
如果一个新文档立马就写入磁盘也会影响性能,所以新的文档首先被添加到内存索引缓存
中,然后写入基于磁盘的段中,接着包含这个新段的提交点也会被写入磁盘,接着磁盘同步,把文件系统缓存中的文档刷新到磁盘,接着新段被打开,使其可以被搜索到,接着内存索引缓存被清空,等待接受新的文档。
注意
段其实就是倒排索引,其不能被改变。当文档发生改变时,每个提交点会包含一个
.del
文件,文件中会列出这些被删除文档的段信息。所以文档被删除时,并不会立马被删除,只是被标记了,其依然可以被搜索到,只不过在结果集返回前就已经移除了。
这里有一个新的问题就是磁盘同步是一件很消耗资源的事情,如果每次插入一个文档都去执行的话,就会造成很大的性能问题。这意味着我们再插入新文档时,不能立马就进行同步。
我们要知道在Elasticsearch
和磁盘之间是文件系统缓存。
新文档 写入 内存索引缓存
中,接着会写入到新段上,重点来了,这个新段不会直接写入磁盘而是先写入到文件系统缓存
中,稍后才会写入到磁盘
。只要文件已经在缓存中,就可以像其他文件一样被打开和读取了。
refresh
在elasticsearch
中,写入和打开一个新段(倒排索引)的轻量的过程叫做refresh
。也就是把内存索引缓存
中的数据写到新段的一个过程。由于elasticsearch
默认每个分片是每秒自动刷新一次。这就造成其索引是近实时搜索而不是实时搜索。最长需要等待1秒。
目前处于性能考虑,我们文档还放在文件系统缓存
中,如果不进行同步到磁盘的话,假设断电了,数据就丢失了。而由于上面也说明,同步不是实时的,是每个一段时间同步一次,默认是30分钟一次。假设elasticsearch
9点同步过了,下次需要在9点半同步,而此时刚好断电了,这时内存里的数据就全部丢失了。但是我们又不想丢失掉。所以elasticsearch
引入了translog
或者叫事务日志。
那么其最终的流程就变成了:
1、一个文档被索引(插入)后,就会被添加到内存缓存区,并且追加到translog
;
2、分片默认是每秒刷新一次;即把内存缓存区里的数据刷新写入到新段中(文件系统缓存中
),此时没有进行fsync
(磁盘同步)。
3、这个新段会被打开并且使其可被搜索。
4、内存缓存区被清空。
5、这个进程会继续工作,更多的文档被添加进内容缓存区和追加事务日志。
6、这也就意味着每隔一段时间translog
会变得越来越大。这时(倒排)索引被flush
;与此同时一个新translog
会被创建,并且一个全量提价被执行:
①所有在内存缓存区的文档都被写入到新段。
②缓存区被清空
③一个提交点被写入硬盘
④文件系统缓存通过fsync
被刷新(flush
)
⑤老的translog
被删除
translog
提供所有还没有被刷到磁盘的操作的一个持久化纪录。当 Elasticsearch 启动的时候, 它会从磁盘中使用最后一个提交点去恢复已知的段,并且会重放 translog
中所有在最后一次提交后发生的变更操作。
translog
也被用来提供实时 CRUD
。当你试着通过ID查询、更新、删除一个文档,它会在尝试从相应的段中检索之前, 首先检查 translog 任何最近的变更。这意味着它总是能够实时地获取到文档的最新版本。
flush
对translog
进行截断并进行提交的行为在elasticsearch
中称作flush
。分片每30分钟被自动刷新(flush
),或者在 translog
太大的时候也会刷新。请查看 translog
文档 来设置,它可以用来 控制这些阈值。
这时其实还有个小问题,要是translog
在断电之前还没有写入磁盘,这时,数据照样还是会丢失。默认情况下translog
是每5秒fsync
到磁盘或者在每次写请求完成之后。对于这种情况,官方说明:但是对于一些大容量的偶尔丢失几秒数据问题也并不严重的集群,使用异步的 fsync 还是比较有益的。
由于自动刷新refresh
每秒创建一个段,这样就会造成短时间内,段数量暴增。而段数目太多会带来较大的麻烦。 每一个段都会消耗文件句柄、内存和cpu运行周期。更重要的是,每个搜索请求都必须轮流检查每个段;所以段越多,搜索也就越慢。
Elasticsearch
通过在后台进行段合并来解决这个问题。小的段被合并到大的段,然后这些大的段再被合并到更大的段。
段合并的时候会将那些旧的已删除文档 从文件系统中清除。 被删除的文档(或被更新文档的旧版本)不会被拷贝到新的大段中。
启动段合并不需要你做任何事。进行索引和搜索时会自动进行。
1、 当索引的时候,刷新(refresh)操作会创建新的段并将段打开以供搜索使用。
2、 合并进程选择一小部分大小相似的段,并且在后台将它们合并到更大的段中。这并不会中断索引和搜索。
合并完成时的活动:
- 新的段被刷新到了磁盘。
- 写入一个提交点,该提交点包含新段且排除旧的和较小的段
- 新的段被打开用来搜索
- 老的段被删除
合并大的段需要消耗大量的I/O和CPU资源,如果任其发展会影响搜索性能。Elasticsearch在默认情况下会对合并流程进行资源限制,所以搜索仍然 有足够的资源很好地执行。
参考地址:
https://www.elastic.co/guide/cn/elasticsearch/guide/cn/merge-process.html
https://www.elastic.co/guide/en/elasticsearch/guide/current/making-text-searchable.html
这篇关于elasticsearch内部原理自我总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!