深入理解es数据写入-查询数据-删除数据
最近面试了很多的同学,看到很多同学的简历里面都写了es相关的技术栈,但是大部分都停留在es的基本api的使用上,对于es的实现原理或者线上的一些基本的事故的解决方案都不是很懂,所以接下来我们一起来聊一下es的具体的实现。
首先,大家都知道es是一个分布式的搜索框架,他会有primary shard和replica shard的,同时他的parimary shard和replica shard不能在同一台机器上,那么我们部署三台服务器来讲解,具体部署情况如下:
备注:实际的分片情况是由es自己分配不一定是规律的
数据写入
有了上面的基础环境以后,如果我们有一个java的客户端想写入一个数据到es,那么他会如何进行数据写入呢?
首先,我们需要明确的是java客户端需要连接哪一个服务器呢?es的数据存储是分配进行数据存储的,其实我们在存储的时候也不知道他会被存储在哪个服务器上,所以无需刻意去关注,我们可以随意连上一个es节点都是可以的,被我们连上的es节点被称为coordinate node(协调节点),该节点接收到数据以后会根据hash算法来计算当前的数据应该被存储到哪个primary shard分片上,如果不是本服务器节点,那么他会把请求转发给对于的服务器节点,那么问题来了,当前节点是怎么知道其他节点的信息呢?其实es的每一个节点都保存了其他的节点的数据,这些数据我们称为元数据。
经过以上过程,我们就介绍完了es存储数据的基本的过程,当然这不是我们的重点,我们的重点是es的底层实现原理,那么节点来我们一起来看下node1的primary shard接受到数据以后他会如何来进行数据的处理:
a. 当primary shard接收到数据以后他会把数据写入到es的buffer缓存中,同时会想操作系统的cache中写入一条translog,同时此时的es是搜索不到这条数据的,也就是说当你java客户端收到了写入成功的消息以后你任然可能搜索不到数据
b. 因为es-buffer是在es中,他的内存是受限的所以我们不可能无限制的向该内存中写入数据,所以该内存中的数据写入满了或者每隔一秒钟都会refresh一次,他会把缓存中的数据刷入操作系统的缓存
c. 把操作系统的缓存数据写入本地磁盘的segment file中,每次写入都会生成一个新的segment file所以系统中会产生大量的segment file,后面会介绍该问题如何解决,同时还会写入commit point到本地磁盘,该文件里面存储的主要是当前的数据写入到哪一个segment file文件中了,执行完该过程数据就可以被搜索到了
d. 数据在写入es-buffer的时候还会写入一条日志文件到os-cache,此cache中的数据满了或者每隔5秒钟都会刷新一次数据到本地磁盘中,此次其实就会出现数据丢失的情况,因为es-buffer和translog的cache都是基于内存的,如果此时机器挂了数据就可能会丢失
至此就详细介绍了es的数据写入的完成过程,当然其中还有一个小的问题就是es会生成大量的segment file文件,他是如何处理的呢?这个知识点我们在数据删除中进行介绍。
数据删除
在操作es的过程中,数据的删除操作也是常有的事情,那么在海量数据中es是如何删除数据的呢?同时又是如何保证删除的性能的呢?
es在删除数据的时候并没有真正的去删除该数据,他是把数据数据的id写入到了一个del文件中,当我们查询的时候也会去查看查询的数据是否在该文件中存在,如果存在则表示已经表删除了,不在返回对应的数据,所以es的删除不是物理删除,那么问题来了-es的数据在什么时候会被物理删除呢?因为不删他一定会占用磁盘空间,所以最终肯定还是会删除的,其实他的删除发生在segment file的merge(合并)的过程中,之前我们说过es会产生大量的segment file,当到达一定的数据的时候es就会自动的把小的segment file合并成一个大的segment file,此时就会牵涉到数据的读取与写入,在写入的时候会首先检查该数据是否在del的文件中,如果在则表示已经删除不进行写入,如不在则写入,这样就实现了es的数据的最终删除操作。
数据查询
数据查询是我们用的最多的,查询有两种情况,一种情况是通过id进行精准查询,还有一种是通过关键词进行搜索。
通过id进行查询: 这个比较简单,任意连上一台es节点,然后根据hash找到对应的分片,该分片可能是replica shard也可能是primary shard,从分片中查询到数据直接返回即可。
关键字搜索: 这个稍微麻烦一点,也是先连上任意一台es节点,然后找到想所有的分片发起关键词查询,其他节点把数据返回到当前节点并进行汇总,汇总以后在返回给用户。
当然如果使用的是分页搜索,那么数据量大了以后还会出现深度分页的问题,这不是本文的重点就不再详细的介绍了,简单的例举一下解决的方案
分页方式 | 性能 | 优点 | 缺点 | 场景 |
from + size | 低 | 灵活性好,实现简单 | 深度分页问题 | 数据量比较小,能容忍深度分页问题 |
scroll | 中 | 解决了深度分页问题 | 无法反应数据的实时性(快照版本)维护成本高,需要维护一个 scroll_id | 海量数据的导出(比如笔者刚遇到的将es中20w的数据导入到excel)需要查询海量结果集的数据 |
search_after | 高 | 性能最好不存在深度分页问题能够反映数据的实时变更 | 实现复杂,需要有一个全局唯一的字段连续分页的实现会比较复杂,因为每一次查询都需要上次查询的结果 | 海量数据的分页 |