Previous: elasticsearch外用与内观(四)-当搜索时,elasticsearch都在做什么(下)
当我们在使用es时,会对他会表现出的各种现象感到疑惑:
![4a5459b17243aa03cac70967cb3c6dcd.png](https://img-blog.csdnimg.cn/img_convert/4a5459b17243aa03cac70967cb3c6dcd.png)
为什么es插入文档后进行搜索不是准实时的而是近实时的?
为什么针对文档的crud(增删改查)操作又是准实时的?
为什么es能保证在修改数据时断电数据也不会丢失?
为什么删除了文档,也没有立即释放磁盘空间?
带着这些疑问,我们继续看下es的内部实现。
es在插入文档的时候,并不是直接把数据写到磁盘,而是先写到他在内存里创建的一个索引缓冲区,index buffer里:
![4d4ee58ca0d8a37cbb2f2c17a559e7d7.png](https://img-blog.csdnimg.cn/img_convert/4d4ee58ca0d8a37cbb2f2c17a559e7d7.png)
我们知道搜索是需要在segment里的倒排索引里搜的,而现在数据都还在index buffer里,所以刚刚插入的文档是搜不到的:
![9cbfb84cc6687f44c0cbfa8513476dfd.png](https://img-blog.csdnimg.cn/img_convert/9cbfb84cc6687f44c0cbfa8513476dfd.png)
这时es需要通过一个refresh操作,在内存里创建一个segment,并把index buffer里的数据插到segment里,然后把index buffer清空:
![89b974f391b61cf7461fe5262f775f6a.png](https://img-blog.csdnimg.cn/img_convert/89b974f391b61cf7461fe5262f775f6a.png)
由于此时segment是在内存里直接创建的,所以刚才插入的文档现在可以被搜索到了。
然而内存里的segment数据需要刷到磁盘里存储才算插入成功,所以refresh会继续调用系统的fsync方法,把内存里的segment刷到磁盘,这样才完成一个完整的refresh操作:
![2ce37679f20c6cbfc4b350abbbb78b4b.png](https://img-blog.csdnimg.cn/img_convert/2ce37679f20c6cbfc4b350abbbb78b4b.png)
因为调用了fsync这种写磁盘方法,会消耗很大的资源,所以为了提升性能,需要尽量减少对fsync的调用, 因此es采用一秒钟执行一次refresh操作:
![0effc09d012ce03554abbbe0f06b8e71.png](https://img-blog.csdnimg.cn/img_convert/0effc09d012ce03554abbbe0f06b8e71.png)
这就导致了从插入文档到可被搜索,中间有1s的延迟,从而使得es的搜索功能不是准实时的,而是近实时的。
现在我们知道在插入文档的时候,数据一开始只是存储在内存的index buffer里,需要等1s后,es才自动执行一次refresh操作把数据存储到磁盘,如果在执行refresh之前断电,怎么保证插在内存里的的数据不丢失?
和其他系统的解决方法一样,es也是通过加了一个写translog(事务日志)机制,插入的文档在写入内存的index buffer之后,需要同步把操作数据追加到translog(事务日志)文件里,在index buffer和translog都写入成功后,才给客户端返回写入成功:
![16f399d1465f229a8d30e2183a4e28ab.png](https://img-blog.csdnimg.cn/img_convert/16f399d1465f229a8d30e2183a4e28ab.png)
这样系统在断电重启后,就可以根据translog里的记录进行还原。
translog还有另外一个用处,就是在每次用_id对文档做crud操作的时候,都先在translog里查,如果translog里没有,再执行refresh操作,从segment里查,这样就可以实现对文档的crud(增删改查)操作达到准实时:
![0f91df3c3a622f905470f3b7eaf87abf.png](https://img-blog.csdnimg.cn/img_convert/0f91df3c3a622f905470f3b7eaf87abf.png)
因为断电恢复和文档crud操作都要依赖translog文件,所以如果一直写一个translog文件导致文件过大也会影响效率,es每隔30分钟,或者tranlog文件变的太大,会执行一个flush操作,对translog文件里的记录进行提交,然后新起一个translog文件,把旧的translog文件删掉:
![bfcb172482f6149de4c9ab4ed0e1e992.png](https://img-blog.csdnimg.cn/img_convert/bfcb172482f6149de4c9ab4ed0e1e992.png)
虽然es对flush操作、refresh操作都提供了restful接口,但是我们尽量只把refresh和flush接口当成系统维护工具在排查问题时使用。
最后为什么删除了文档数据,也没有立即释放磁盘空间? 因为es在删除一个文档的时候,是通过标记删除的方式处理的,删除一个文档的时候,会在一个.del后缀的文件里标注是哪个segment里的哪个文档被删除了:
![b4524ab5c3af9aeb1c4eb18ed79fe0ab.png](https://img-blog.csdnimg.cn/img_convert/b4524ab5c3af9aeb1c4eb18ed79fe0ab.png)
我们知道refresh每秒执行一次,都会在磁盘里生成一个segment,这么多小的segment不但要占用系统文件句柄,操作的时候还要浪费IO和cpu资源:
![accfb21698e1b5def58cda820829fcbd.png](https://img-blog.csdnimg.cn/img_convert/accfb21698e1b5def58cda820829fcbd.png)
所以es在后台专门启动了负责segment合并的线程,把小segment合并成大segment,在合并过程中会把在.del文件里标注的文档直接pass掉,然后把小的segment删掉,所以es需要完成segment合并后,才会释放磁盘空间:
![ed93a19aa82a24b32815199080f9f729.png](https://img-blog.csdnimg.cn/img_convert/ed93a19aa82a24b32815199080f9f729.png)