整体流程介绍
首先客户端发一个请求,要写数据(增删改),数据是存在索引index中的,索引分成若干个分片(shard),不管是主分片还是副分片,都是存在硬盘上的,落实成文件,这是必然流程,一定是这样的,但是要做写操作的时候,往硬盘里面写数据,是I/O操作,重量级操作,很慢,不符合ES的近实时(NRT,Near RunTime)特性,无论是写还是查,1秒内返回。要是使用I/O流,未必能达到1秒返回。
默认 ES会在内存里面开一个缓存,缓存的大小无限制,但里面只存1秒的数据,记录完一秒的数据,下一秒重新开一个缓存来记录下一秒的数据, 这一秒记录好的数据,会放在内存的另外一个地方-分段(segment),类似java里面的File,一秒的数据一个分段,记录一段时间后,内存里面的分段数量明显变多,然后ES会请求操作系统协作,每隔一段时间会把分段交给系统缓存(OS cache)管理工具,每个操作系统都有缓存管理工具,不管是windows 还是linux,缓存管理基于虚拟内存(拿硬盘做缓存)和内存缓存两个一起用。
ES就会把分段交给操作系统,让它协作管理,在操作系统的系统缓存区里面放了很多分段(segment),时间不断变长后,缓存里面的分段数量也会变多,操作系统拥有全资源控制能力,但是资源是有限的,不可能无限使用。然后它也会把数据往硬盘里面刷,刷到硬盘里面是一个个的分段, 就会变成文件,但是文件的数量是很多的。
查询性能高的原因
这么多分段文件,ES这么处理的目的就是,查询。每秒一个缓存,缓存里的数据会被立刻查到,上一秒的数据在分段里面,分段里面的文件也是被开启状态(OPEN)也可以做搜索查询,在操作系统缓存里面放置的分段文件也是开启的,也可以做查询操作,在硬盘里面的文件同样是这样,也可以做搜索查询。它又是多线程操作,硬盘上的文件由长期I/O去管理。这就是ES查询快的原因。
但是在整个流程里面会有问题,
1. 断电,丢数据(缓存中的数据)情况问题
不管是缓存,还是分段(segment),系统缓存(OS cache),一旦电脑断电,硬盘还没有存数据,就会丢数据。ES为了这个情况发生,它会在写操作的同时,也会记录translog日志文件(记录命令),日志永久在硬盘上,而且是长I/O,长期定位。万一断电发生,在重启的时候会全看日志,把日志的命令还原到内存里面。
2.硬盘文件数量多问题
硬盘的文件数量会随着时间的变长而变多,所以ES会在每隔一段时间或者空闲的时候,会把系统硬盘里面大小相近的分段文件读到内存里,然后合并,合并成一个更庞大的segment文件,在合并过程中,把所有已标记删除的数据清除,把文件压缩,压缩完后,还是交给系统缓存(OS cache),所以硬盘上的文件是逐渐变多,再经由合并,再逐渐变少,如果长期没有写操作,最终会变成一个分片(一个文件),不是一个索引一个文件,一个分片若干个分段。
3.日志文件容量问题
随着时间的增长,日志文件的大小也会吃尽硬盘的所有容量,ES会在日志处做一个周期化的提交(commit point)比如说合并文件的周期30分钟,那么translog的提交周期就是30分钟,只要把数据写到硬盘里面,说明没有问题了,ES就会把老的命令数据清除,这样最极限的情况就是存60分钟的命令,不至于发生把硬盘吃尽的情况发生,
总结
不考虑文件是否排列紧密,先做标记删除和再新增,使用周期化处理,读取硬盘中大小相近的分段文件做合并整理,合并整理的时候清除标记删除的数据,做一下压缩化处理,按照整体流程不断循环往复,让自己的应用文件数量保持在一个合理范围。缓存中,硬盘中的文件都是可搜索状态(OPEN)让增删改查都是近实时(NRT,Near RunTime)处理,达到秒级处理