本套技术专栏是作者(秦凯新)平时工作的总结和升华,并深度整理大量网上资源和专业书籍。通过从真实商业环境抽取案例进行总结和分享,并给出商业应用的调优建议和集群环境容量规划等内容,请持续关注本套博客。QQ邮箱地址:1120746959@qq.com,如有任何学术交流,可随时联系。
1 ES 分布式架构设计
elasticsearch设计的理念就是分布式搜索引擎,底层其实还是基于lucene的。
es中存储数据的基本单位是索引,比如说你现在要在es中存储一些订单数据,你就应该在es中创建一个索引,order_idx,所有的订单数据就都写到这个索引里面去,一个索引差不多就是相当于是mysql里的一张表。
-> index(mysql里的一张表)
-> type(一个index里可以有多个type,每个type的字段都是差不多的,但是有一些略微的差别。
比如在订单index里,建两个type,一个是实物商品订单type,一个是虚拟商品订单type,
这两个type大部分字段是一样的,少部分字段是不一样的)
-> mapping (mapping就是这个type的表结构定义)
-> document (一条document就代表了mysql中某个表里的一行,每个document有多个field,每个field就代表了这个document中的一个字段的值)
-> field(具体索引字段)
复制代码
- 设计一个索引,这个索引可以拆分成多个shard,每个shard存储部分数据。
- 每一个shard的数据实际是有多个备份,每个shard都有一个primaryshard,负责写入数据,但是还有几个replica shard。primary shard写入数据之后,会将数据同步到其他几个replica shard上去。
- es集群多个节点,会自动选举一个节点为master节点,这个master节点其实就是干一些管理的工作的,比如维护索引元数据拉,负责切换primary shard和replica shard身份等。
- 如果master节点宕机了,那么会重新选举一个节点为master节点。
- 如果是非master节点宕机了,那么会由master节点让那个宕机节点上的primary shard的身份转移到其他机器上的replica shard。
2 ES 写数据过程深入剖析
2.1 ES 写数据总体流程
- 客户端选择一个node发送请求过去,这个node就是coordinating node(协调节点)
- coordinating node,对document进行路由,将请求转发给对应的node(拥有primary shard 的节点)
- 拥有primary shard 的node处理写入请求,然后将数据同步到replica node
- coordinating node,如果发现primary node和所有replica node都搞定之后,就返回响应结果给客户端。
2.2 ES 写数据内核原理剖析
-
步骤一(buffer缓冲和写入translog日志阶段):ES 写数据先写入buffer,在buffer里的时候数据是搜索不到的,并同时将数据写入translog日志文件。
-
步骤二(refresh刷写os cache阶段):当写入数据buffer快满了,或者到一定时间,就会将buffer数据refresh到一个新的segment file中,但是此时数据不是直接进入segment file的磁盘文件的,而是先进入os cache的,这个过程就是refresh。segment file位于os cache中。
(1) 每隔1秒钟,es将buffer中的数据写入一个新的segment file, 每秒钟会产生一个新的磁盘文件,segment file,这个segment file 中就存储最近1秒内buffer中写入的数据。 (2) 如果buffer里面此时没有数据,那当然不会执行refresh操作,每秒创建换一个空的 segment file,如果buffer里面有数据,默认1秒钟执行一次refresh操作,刷入一个 新的segment file中 (3) 只要buffer中的数据被refresh操作,刷入os cache中,就代表这个数据就可以被搜索到了 (4) 为什么叫es是准实时的?NRT,near real-time,准实时。 默认是每隔1秒refresh一次的,所以es是准实时的,因为写入的数据1秒之后才能被看到。 (5) 可以通过es的restful api或者java api, 手动执行一次refresh操作,就是手动将buffer中的数据刷入os cache中,让数据立马就可以被搜索到。 复制代码
-
步骤三(清空buffer阶段):只要数据被输入os cache中,buffer就会被清空了,因为不需要保留buffer了,数据在translog里面已经持久化到磁盘去一份了。
-
步骤四(commit阶段flush操作):重复1~3步骤,新的数据不断进入buffer和translog,不断将buffer数据写入一个又一个新的segment file中去,每次refresh完buffer清空,translog保留。随着这个过程推进,translog会变得越来越大。当translog达到一定长度的时候,就会触发commit操作。
(1)commit操作发生第一步,就是将buffer中现有数据refresh到os cache中去,清空buffer (2)将一个commit point写入磁盘文件,里面标识着这个commit point对应的所有segment file (3)强行将os cache中目前所有的数据(所有的segment file文件)都fsync到磁盘文件中去。 (4)将现有的translog清空,然后再次重启启用一个translog,此时commit操作完成。 默认每隔30分钟会自动执行一次commit,但是如果translog过大,也会触发 commit。整个commit的过程,叫做flush操作。我们可以手动执行flush操作, 就是将所有os cache数据刷到磁盘文件中去。 (5)es中的flush操作,就对应着commit的全过程。我们也可以通过es api, 手动执行flush操作,手动将os cache中的数据fsync强刷到磁盘上去, 记录一个commit point,清空translog日志文件。 (6)translog其实也是先写入os cache的,默认每隔5秒刷一次到磁盘中去,所以默认情况下, 可能有5秒的数据会仅仅停留在buffer或者translog文件的 os cache中,如果此时机器挂了,会丢失5秒钟的数据。但是这样性能比较好,最多 丢5秒的数据。也可以将translog设置成每次写操作必须是直接fsync到磁盘,但是性能会差很多。 (7) es是准实时的,数据写入1秒后可以搜索到;但是却可能会丢失数据,因为 有5秒的数据停留在buffer、translog os cache、segment file os cache中, 有5秒的数据不在磁盘上,此时如果宕机,会导致5秒的数据丢失。 (8) 如果你希望一定不能丢失数据的话,你可以设置个参数,官方文档,百度一下。 每次写入一条数据,都是写入buffer,同时写入磁盘上的translog,但是 这会导致写性能、写入吞吐量会下降一个数量级。本来一秒钟可以写2000条, 现在你一秒钟只能写200条,都有可能。 复制代码
-
步骤五(删除操作):commit的时候会生成一个.del文件,里面将某个doc标识为deleted状态,那么搜索的时候根据.del文件就知道这个doc被删除了
-
步骤六(更新操作):就是将原来的doc标识为deleted状态,然后新写入一条数据
-
步骤七 (merge阶段操作):buffer每次refresh一次,就会产生一个segment file,所以默认情况下是1秒钟一个segment file,segment file会越来越多,此时会定期执行merge。 每次merge的时候,会将多个segment file合并成一个,同时这里会将标识为deleted的doc给物理删除掉,然后将新的segment file写入磁盘,这里会写一个commit point,标识所有新的segment file,然后打开segment file供搜索使用,同时删除旧的segment file。
3 ES 读数据过程深入剖析
- 步骤一:客户端发送请求到一个coordinate node(随机选择)
- 步骤二:协调节点将搜索请求转发到所有的shard对应的primary shard或replica shard(不同的副本都可以用来搜索)。
- 步骤三:query阶段,每个shard将自己的搜索结果(其实就是一些doc id),返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,产出最终结果
- 步骤四:fetch阶段,接着由协调节点,根据doc id去各个节点上拉取实际的document数据,最终返回给客户端
4 总结
在此感谢石杉的讲义,结合大数据在我们工业大数据平台的实践,总结成一篇实践指南,方便以后查阅反思,后续我会根据本篇博客进行代码技术实践实现。
秦凯新 于郑州 201903032152