一 序
本文属于极客时间Elasticsearch核心技术与实战学习笔记系列。
二 分片的内部原理
什么是 ES 的分片
- ES 中最小的工作单元 / 是一个 Lucence 的 Index
一些问题:
- 为什么 ES 的搜索时近实时的(1 秒后被搜到)
- ES 如何保证在断电时数据也不会丢失
- 为甚删除文档,并不会立刻释放空间
2.1 倒排索引的不可变性
倒排索引采用 Immutable Design, 一旦生成,不可更改
不可变性,带来了的好处如下:
- 不许考虑并发写文件的问题,避免了锁机制带来的性能问题
- 一旦读入内核的文件系统缓存,便留在那里,只要文件系统存有足够的空间,大部分请求就会直接请求内存,不会命中磁盘,提高了很大的性能
- 缓存容易生成和维护 / 数据可以被压缩
不可变更性,带来了的挑战:如果需要让一个新的文档可以被搜索,需要重建整个索引
2.2 Lucence Index
分片是Elasticsearch中的最小工作单元,本质上是一个Lucene Index。在Lucene中,单个倒排索引文件被称为Segment,Segment是自包含且不可变更的,多个Segment汇总在一起称为Lucene Index
当有新文档生成时,会生成新的Segment,查询时会同时查询所有Segment,并且对结果汇总,Lucene中有一个文件用来记录所有Segments信息,叫做Commit Point,删除的文档信息,保存在.del文件中。搜索的时候,会对删除的文档做过滤。
2.3 Refresh
- 将 Index buffer 写入 Segment 的过程叫做 Refresh。Refresh 不执行 fsync 操作
- Refresh 频率:默认 1 秒发生一次,可通过 index.refresj_interval 配置。Refresh 后,数据就可以被搜索到了。这也就是为什么 ES 被称为近实时搜索
- 如果系统有大量的数据写入,那就会产生很多的 Segment
- Index Buffer 被占满时,会触发 Refresh, 默认值是 JVM 的 10%
2.4 什么是Transaction Log
- Segment 写入磁盘的过程相对耗时,借助文件系统缓存,Refresh 时,先将 Segment 写入缓存以开放查询
- 为了保证数据不会丢失。所有在 Index 文档时,同时写 Transaction Log,高版本开始,ra 默认落盘。每个分片都有一个 Transaction Log
- 当 ES Refresh 时,Index Buffer 被清空,Transaction Log 不会清空
当ES产生断电的时候,因为Transaction Log都做了落盘的处理,所以系统重启的时候,会先从Transaction Log 进行recover。不会丢失。
2.5 什么是Flush
ES Flush & Lucene Commit
调用 Refresh ,Index Buffer 清空并且 Refresh
调用 fsync, 将缓存中的 Segments 写入磁盘
清空(删除)Transaction Log
默认 30 分钟调用一次
Transaction Log 满(默认 512M)
2.6 Merge
Segment
很多,需要定期被合并- 减少
Segment
/ 删除已经删除的文档(真正的删除)
- 减少
- ES 和 Lucene 会自动进行 Merge 操作(强制merge)
- POST my_index/_forcemerge
***********************
有优秀的小伙伴Geek_817ea4查了文档,做了总结,这里引用下
1)客户端发起数据写入请求,对你写的这条数据根据_routing规则选择发给哪个Shard。
确认Index Request中是否设置了使用哪个Filed的值作为路由参数,
如果没有设置,则使用Mapping中的配置,
如果mapping中也没有配置,则使用_id作为路由参数,然后通过_routing的Hash值选择出Shard,最后从集群的Meta中找出出该Shard的Primary节点。
2)写入请求到达Shard后,先把数据写入到内存(buffer)中,同时会写入一条日志到translog日志文件中去。
当写入请求到shard后,首先是写Lucene,其实就是创建索引。
索引创建好后并不是马上生成segment,这个时候索引数据还在缓存中,这里的缓存是lucene的缓存,并非Elasticsearch缓存,lucene缓存中的数据是不可被查询的。
3)执行refresh操作:从内存buffer中将数据写入os cache(操作系统的内存),产生一个segment file文件,buffer清空。
写入os cache的同时,建立倒排索引,这时数据就可以供客户端进行访问了。
默认是每隔1秒refresh一次的,所以es是准实时的,因为写入的数据1秒之后才能被看到。
buffer内存占满的时候也会执行refresh操作,buffer默认值是JVM内存的10%。
通过es的restful api或者java api,手动执行一次refresh操作,就是手动将buffer中的数据刷入os cache中,让数据立马就可以被搜索到。
若要优化索引速度, 而不注重实时性, 可以降低刷新频率。
4)translog会每隔5秒或者在一个变更请求完成之后,将translog从缓存刷入磁盘。
translog是存储在os cache中,每个分片有一个,如果节点宕机会有5秒数据丢失,但是性能比较好,最多丢5秒的数据。。
可以将translog设置成每次写操作必须是直接fsync到磁盘,但是性能会差很多。
可以通过配置增加transLog刷磁盘的频率来增加数据可靠性,最小可配置100ms,但不建议这么做,因为这会对性能有非常大的影响。
5)每30分钟或者当tanslog的大小达到512M时候,就会执行commit操作(flush操作),将os cache中所有的数据全以segment file的形式,持久到磁盘上去。
第一步,就是将buffer中现有数据refresh到os cache中去。
清空buffer 然后强行将os cache中所有的数据全都一个一个的通过segmentfile的形式,持久到磁盘上去。
将commit point这个文件更新到磁盘中,每个Shard都有一个提交点(commit point), 其中保存了当前Shard成功写入磁盘的所有segment。
把translog文件删掉清空,再开一个空的translog文件。
flush参数设置:
index.translog.flush_threshold_period:
index.translog.flush_threshold_size:
#控制每收到多少条数据后flush一次
index.translog.flush_threshold_ops:
6)Segment的merge操作:
随着时间,磁盘上的segment越来越多,需要定期进行合并。
Es和Lucene 会自动进行merge操作,合并segment和删除已经删除的文档。
我们可以手动进行merge:POST index/_forcemerge。一般不需要,这是一个比较消耗资源的操作。merge一般不需要人为参与,es自动会做。索引从hot迁移到warm,可以人为对索引做一次force merge
这一届真的知识点很多。