shard是es中的名称,在lucene中叫index。以下皆是lucene中的索引运行原理。
每个segment都是一个inverted index倒排索引,index索引实际是由1到多个的segment组成,在index层次上,有个commit point提交点,其包含当前生效的所有的segment的名称。在查询时,lucene将按照segment从旧到新的顺序加载所有数据
当一个新的文件需要索引时,其首先是被保存在内存中。当index被flush时,内存中的新的数据将被保存到一个新的segment中,并生成一个新的commit point文件,这个文件包含新的segment。
这时新的segment将被open,确保新的数据能被查询到。而后缓存被清空,准备接收新的文档
删除和更新
segment是不可更改的,所以文档是无法从segment中删除,同样也不能被更新。每个commit point里有个.del的文件,里面记录被删除的文档名。当一个文档被执行删除时,其仅是在.del文件中标记为删除, 而被标记为删除的文件还是可以被搜索到,只是在返回的时候被过滤了.
更新和删除类似,就是将旧的数据标记为删除,把新的数据加入到新的segment中
刷新索引 refresh
前面说过,要想让新的文档能被搜索,需将缓存中的数据写到文件中,而保存到物理文件中是非常耗费性能的,特别是写的很频繁。在内存到物理文件之间,实际还有一个状态是文件系统缓存,当将内存数据写到文件系统缓存后,其就可以像其的文件一样被读取,而写到文件系统缓存是一个轻量的操作,写到文件系统缓存的操作就叫refresh。在es中,默认1秒refresh一次,就也是为什么官方号称准实时的地方。
手动触发refresh
POST /_refresh
POST /blogs/_refresh
但即使轻量,那refresh频繁,对性能还是会有不小的影响,可自行设置refresh频率
PUT /my_logs { "settings": { "refresh_interval": "30s" }} PUT /my_logs/_settings { "refresh_interval": -1 } //禁用 PUT /my_logs/_settings { "refresh_interval": "1s" }
持久化 flush
正如前面所说,refresh仅是将数据保存在文件系统的缓存中,并非物理保存,这还是一个比较危险的。所以es引入了一个translog,每一个写操作将会保存进translog中
每次refresh操作,将会清空in-memory buffer,而不会去清translog
translog继续接收新的写操作
当translog足够大时,es自动执行flush操作,将数据物理保存,此时新建一个新的translog来接收新的操作,并删除旧的
手动触发flush操作
当然,正常是不需要手动触发flush操作
segment合并 optimize
随着reflesh的频繁操作,将不需要花多长时间就造成了segment数量的爆炸式增长。大量的segment将造成资源的浪费和搜索性能的降低。es通过将小的segment合并成一个大的segment,并在剔除被标记为删除的文档
合并过程,并不影响搜索和索引。一旦合并完成将会删除旧的segment
合并将会使用大量的cpu和io,所以会造成一定的性能下降。es会限制合并的过程以保证搜索有足够的资源执行
optimize操作将会强制合并segment,合并为max_num_segments 个数量的segment。正常不需要用户手动触发optimize,不过有些场景手动触发是不错的选择,比如某个索引已经不会再去更新了,如按天、星期、月保存的日志,此时将其合并为一个segment是个不错的选择
POST /logstash-2014-10/_optimize?max_num_segments=1
手动触发optimize将会毫无保留的使用集群资源去执行,不像es自动触发会进行限制,这将有可能造成搜索无响应或缓慢的问题