2020-10-31 本是古典 何须时尚 2022-11-21 11:26 91阅读 0赞 ## Elasticearch和Kafka概念对比 ## 技术标签: [框架][Link 1] [kafka][] [elasticsearch][] [es][] # 1、概念对比 # ![在这里插入图片描述][df36568f14914d5eae2f45b4f45f0876.png] ![在这里插入图片描述][48ea1a6cd2a60d6cdcdc7b6935e34b6e.png] > * kafka集群包含多个broker,每个broker都是一个kafka实例。每个服务器会有多个broker,我们暂且认为每个服务器包含一个broker。 > * ElasticSearch集群包含多个Node,一个ElasticSearch实例称为一个Node。 > * 对于kafka来说,每个broker包含多个Topic,kafka的数据保存在Topic中。 > * ElasticSearch来说,每个Node包含多个shard。 > * 每个Topic划分成多个分区partition,分区的作用是用作负载。 > * 每个shard划分成多个segment. > * 分区具有主从之分,Replication:每一个分区都有多个副本,副本的作用是做备胎。当主分区(Leader)故障的时候会选择一个备胎(Follower)上位,成为Leader。 > * shard具有主从之分,一个分片可以是主分片或者副本分片。 索引内任意一个文档都归属于一个主分片,所以主分片的数目决定着索引能够保存的最大数据量。一个副本分片只是一个主分片的拷贝。 > 副本分片作为硬件故障时保护数据不丢失的冗余备份,并为搜索和返回文档等读操作提供服务。 对于Kafka和ElasticSearch(以下简称ES)大家应该都不会感到陌生,在之前我也有几篇文章有对Kafka和ES进行了介绍。 [Kafka走查][Kafka] [ElasticSearch入门][ElasticSearch] [ElasticSearch——Java API][ElasticSearch_Java API] [ElasticSearch——搜索机制][ElasticSearch 1] [ES选主流程分析][ES] 很多人肯定奇怪我为啥要拿两者进行比较呢,这两者明明没有什么关系或者说不是同一个方向的,一个是**队列**,一个是**存储**。确实不应该一起比较,但是大家看清文章的标题,设计原理,对这里我比较的是在原理,架构方面。两者都是各自领域炙手可热的“选手”,对比两者的设计,实现我觉得很有意义,对于我们自己在架构方面的设计也很有帮助。好了废话说了一堆下面就进入正题吧。 # 概述 # Kakfa和ES都是目前分布式系统中出现频率非常高的组件,而且他们在各自的方向都是非常优秀的。那么首先就快速的介绍一下各自的特性吧。 * Kafka Kafka是一种**高吞吐量**的**分布式**发布订阅**消息系统**,它可以处理消费者规模的网站中的所有动作流数据。 * 通过O(1)的磁盘数据结构提供消息的持久化,这种结构对于即使数以TB的消息存储也能够保持长时间的稳定性能。 * 高吞吐量 ;即使是非常普通的硬件Kafka也可以支持每秒数百万的消息。 * 支持通过Kafka服务器和消费机集群来分区消息。 * 支持Hadoop并行数据加载,支持Spark,Flink做实时流计算。 * ES * 分布式的实时文件存储,每个字段都被索引并可被搜索 * 分布式的实时分析搜索引擎 * 处理PB级结构化或非结构化数据 > **适合场景:** 明细查询、过滤、排序 > **不适合场景:** > 1.大数据量(索引文档总数>=500w)、高基数(桶>=1000)聚合 > 2.大数据量(索引文档总数>=500w)、高基数(唯一值>=10000)去重(去重也是不准确的) > 3.高基数模糊查询(索引文档总数>=500w) > 4.关联查询:es对关联查询支持的不好 > 5.多维度组合聚合(维度>=5):在基数较大的时候,会存在严重的性能问题 > 6.大量导出(>=10w):不适合大批量查询 > 7.数据分析:不适用于离线计算 > **桶数预估:假设A、B字段分别有10个不同值,对A、B两个字段同时分组,则最终会产生10x10=100 桶(各字段不同值的笛卡尔乘积即为最终桶数)** # 分布式环境如何选主 # Kafka和ES作为一种分布式的数据存储服务,而且都是高可用,可扩展的,那么毋庸置疑都是存在多个节点的。那么为了这么多的节点能协同工作,必然会有一个协同节点来负责管理整个集群,这个集群节点在Kafka中称作**controller**(控制器),在ES中我们称作**master**。 ## Kafka broker选主 ## 熟悉kafka的朋友都知道,在kafka的架构中引入了Zookeeper组件(这里就不展开介绍zk了),用于管理集群的元数据,而controller信息也属于元数据之一,所以kafka 控制器选主就是依赖zk实现的,基本原理如下: 集群中的所有broker全部向zk的/controller注册临时节点,第一个成功的就成为控制器brokker,剩下的就注册watcher。如果controller 下线或者崩溃,集群中的其他broker可以瞬间感知,从而重新发起注册。 ## ES master选主 ## ES 的配置文件中有一个属性“node.master” ,如果值为**true**表示该节点有资格成为master节点。那么ES的选主原理就是:在有资格成为master节点的中相互发起Ping请求,然后在本节点中找出ID最小的节点,向其发起投票,当某个节点收到的投票超过半数是,则该节点成为master节点,并向集群中的所有节点进行信息广播。 以上就是对Kafka Broker和ES 选主做的简单介绍,其中Kafka因为使用了ZK所以很简单,而ES是自己实现了一个Bully算法,具体详细的可以看[ES选主流程分析。][ES] # 数据模型 # Kafka的数据是按Topic分组存在Partition上面,ES的数据是按Index/Type(马上ES会去除)分组存在Shard上面。从这个角度看两者很相似,另外两者使用的都是**主从模式**。Partiton和Shard都有一个主副本,然后根据副本因子创建多个从副本。 ## 主副本选举 ## 两者类似。 ### Kafka leader选举 ### kafka使用的是优先副本策略来进行Partition主副本选举的: > 在创建topic时会自动创建一个AR(All Replication)集合,这个AR集合在加入新的Partition分区之前是不会变化的,包括副本分区下线等异常情况;另外会有一个ISR(in-sync-replication)同步副本集合,这个集合是当前和主副本数据保持同步的副本集合,这个集合是会随环境变化的。因此每次选举主副本leader时会依次从AR集合中拿出副本,并判断是否在ISR集合中,如果在则选举成功,并将本次leader信息同步到zk中。 ### ES ### ES从5.x版本开始引入了Allocation ID是的概念,用于主分片选举概念,每个分片有自己的唯一的Allocation ID,同时集群元信息中有个列表记录了哪些分片拥有最新的数据。在创建索引时,指定哪个分片作为主分片具有很大的灵活性,会将集群的均衡和其他约束(如分配感知级过滤器等)考虑在内,甚至可以人为的去控制,但是有一个原则就是必须保证主分片的数据是最新的。 > ES中也有一个同步副本集合,类似kafka中ISR,在ES中时in-sync-allocation IDs。 所以选举的主分片也必须在isr中才行。 ## 主从数据同步 ## 因为主从模式,所以每个从节点都是均匀的分配在所有的集群节点上的,这必然会带来数据一致性,以及可用性的问题,kafka和ES在这方面也都是有自己的实现方式的。 ### Kafka ### **Kafka对数据的读写都是在leader 分片上面执行的**,副本分片主要是为了对数据进行冗余备份,所以对于kafka来说没有数据一致性的问题。但是这并不代表生产者写入的消息数据可以立马被消费者读取到,这里有个HW的问题,后面会详细讲解。 ### ES ### 和Kafka类似,ES的数据存在Shard上面,同样Shard也会进行多副本存储,但是有一个区别,ES会把所有的**写请求**都分发到主分片 primary shard上,但是读请求会在该分片的所有的副本分片(包含主分片)上面进行负载均衡。因此这里就存在了如何保证数据在写入到主分片后能同步到所有的副本分片上。这里回顾[ES数据副本模型][ES 1]就 可以明白了,简单的说就是通过主分片来维护committed list来保证的,个人感觉这个和Kafka的HW很类似,committed list中记录了所有分片最后都提交了的记录,这样保证如果主分片下线后,其他的副本分片也能持有所有数据重新提供服务。 # 数据如何存储 # 讨论到这里,就会涉及到很多底层的原理了,而且这边两者的实现就有很多不同了,毕竟两者的设计目的是完全不一。下面会简单分析一下,后面会单独出文章来讲解两者。 ## Kafka ## 上面说过Kafka是高吞吐量的,之所以高吞吐其数据的存储方式也是原因之一。 ![4ea3f767b4347b9d415fb937ee3ebfc8.png][] 此图出自朱老师(《深入理解Kafka:核心设计与实践原理》的作者朱小厮) 这里就简单的说一下几个重要的文件: * .log 日志文件,用于存储具体的消息数据。另外每个log文件大小是有限制的,在达到指定大小后会创建新的日志文件,文件名也是以当前log中最早的消息的offset来命名的比如00000000000000000000.log。 * .index 偏移量索引文件,和log文件一一对应,文件名与日志文件的名字是一样的,只是文件扩展名为.index。**index文件中并没有为数据文件中的每条Message 建立索引,而是采用了稀疏存储的方式(稀疏索引),每隔一定字节的数据建立一条索引。这样避免了索引文件占用过多的空间,从而可以将索引文件保留在内存中。** * .timeindex 时间戳索引文件,和log文件一一对应,文件名与日志文件的名字是一样的,只是文件扩展名为.timeindex。注意,该文件是在0.8版本之后才有的。 这里就要说明一下,kafka是直接写磁盘文件的,但是是以顺序追加的形式写在文件的末尾,这种方式性能是随机读写的几十倍。除了这个**顺序追加**,Kafka还使用到了**页缓存Page Cache**,**零拷贝Zero-Copy**等技术来进一步提升写性能。 简单的总结一下 > 利用LogSegment+有序offset+稀疏索引+二分查找+顺序查找 可以快速查找指定的消息; > 顺序追加+页缓存+零拷贝 提升写性能。 ## ES ## ES是基于Lucene实现的,而Lucene将数据存储在一个个的segments当中,那么segments中包含了哪些数据呢,又是怎样来支撑ES搜索和聚合的呢? 究其本质来说就是只有两种结构的数据(稠密索引): * 倒排索引:从单词(term)的维度看文档,标识每个term分表出现在哪个文档(docId)当中,以及在每个文档中出现的次数(词频)和位置(相对于文档首部的偏移量); term->docId * 正排索引:从文档的角度看单词,表示每个文档有哪些单词,以及每个单词出现的次数和位置; docId->term 另外当segments写到磁盘后,是不会发生改变的,不变性带来的好处就是: 1. 不需要添加锁,写性能提高 2. 读性能提高,一旦被OS做了文件缓存,那么绝大多数读请求会直接会直接从内存取数据不需要磁盘IO了 3. 其他缓存(query cache ,request cache,filedata cache)性能的提高 缺点就是,当发生修改时,必须重建索引才能被看到; 当一个读请求过来时会遍历所有的segments 来筛选出复合条件的数据。 由于segments是不变的,所以对于删除,在ES中有一个.del文件用来标记哪个segment 哪条记录被删除;对于修改,ES中每个记录都是由版本信息的,当文档更新时旧版本会标记为删除,并创建新版本; 当segments数量过多时,ES后台任务会对这些segment进行合并操作,这时会根据.del文件将被标记删除的记录真正的删除。 # 写过程 # 对于Kafka来说写过程就是集群如何把生产者发送过来的数据写入到本地日志文件中的;而对于ES来说写过程就相对复杂了许多,**这里的写入不包括数据副本的同步**。 ## Kafka ## 上文说过消息在写入时使用的是顺序追加文件,并且不允许修改已经写入的消息;另外kafka也是将数据先写入系统的磁盘缓存(Page Cache),然后等待系统调度将缓存刷入磁盘,当然Kafka 中同样提供了同步刷盘及间断性强制刷盘(fsync)的功能。 ## ES ## ES每个节点在写入数据时会涉及到buffer,translog,refresh,flush,fsync,commit point相关的概念: * buffer: jvm管理的内存区域,有限大小 * translog: 日志文件存储每一个文档 * refresh: 默认每1s执行一次,将buffer中的数据写入到一个新的segment(文件缓存中)中,此时数据可被搜索 * flush: 将buffer中的数据全部写入新的segments中,并将系统文件缓存中的segment刷入磁盘,并生成一个commit point文件同时刷盘 * fysnc: 将translog日志从文件缓存刷入磁盘 * commit point: segment 文件刷入磁盘时生成日志文件,主要记录segment文件信息 下面是具体的写入过程了: 1. 写请求到来好,先写入到buffer中,同步写入到translog文件中(此时会写到文件缓存) 2. 默认每隔1s buffer中的数据,或者buffer大小满了会被refresh到文件缓存中的新segment文件中,同时会清空buffer,此时数据才能被检索到,这也是**近实时搜索**的原因 3. 每隔30分钟,或者translog大小达到一定大小会触发flush操作,flush 会将文件缓存中的segment file全部刷到磁盘上面,同时也会将translog也刷入磁盘;并生成一个commit point的文件,文件中记载 了此次flush 了哪些segment ;最后会清空translog文件; 4. 从上面过程知道translog也是先写入到文件缓存中的,其实在ES的后台每隔5s (可以配置) 会将translog刷入到磁盘,这个操作叫做fsync;**而这5s 内如果机器断电会发生真正的数据丢失** 5. 当磁盘上的segment文件日益增多时,会发生合并(merge)操作,合并过程会根据.del文件删除数据,同时也会生成新的commit point文件; 以上也就是ES数据在分片上面写入的过程了在第4步会发生数据丢失的风险。 > 通过比较ES和Kafka我们可以发现两者都大量的使用了文件缓存(Page Cache)技术,这既可以保证JVM内存的安全和可用,又保证了数据写入高性能。 # 总结 # 通过对比我们可以知道不同设计的原因,通过对比我们可以发现设计的优秀,但是最终目标不仅仅是对“轮子”的熟悉,更重要的是将这些优秀的设计理念融汇到自己的项目架构设计中,这样才能真正的学以致用。 通过多次的更新,目前将Kafka和ES的内部设计原理简单的总结分析了,但是还有很多细节会有所欠缺,比如Kafka数据副本是如何更新的细节,以及读过程(kafka消费,ES搜索)都还没有详细说明,当然后面我会继续更新。 > 努力的码农不仅仅是码农,还是知识的搬运工! [Link 1]: https://www.pianshen.com/tag/%E6%A1%86%E6%9E%B6/ [kafka]: https://www.pianshen.com/tag/kafka/ [elasticsearch]: https://www.pianshen.com/tag/elasticsearch/ [es]: https://www.pianshen.com/tag/es/ [df36568f14914d5eae2f45b4f45f0876.png]: /images/20221120/a2b53b48f21847aaa6a26e60fc3dfa00.png [48ea1a6cd2a60d6cdcdc7b6935e34b6e.png]: /images/20221120/edc6b62f6f9040ad9db9ae158fd646b7.png [Kafka]: https://juejin.im/post/6844903785429467149 [ElasticSearch]: https://juejin.im/post/6844903718291243015 [ElasticSearch_Java API]: https://juejin.im/post/6844903719109165063 [ElasticSearch 1]: https://juejin.im/post/6844903725845184520 [ES]: https://juejin.im/post/6844903908284841992 [ES 1]: https://juejin.im/post/6844903958318678023 [4ea3f767b4347b9d415fb937ee3ebfc8.png]: /images/20221120/6a9d8d3fdc2b4fe1bbefcfba6afdb3dd.png
还没有评论,来说两句吧...