当前位置: 首页 > 后端技术 > Java

网易二面:为什么Kafka吞吐量大、速度快??

时间:2023-04-02 00:46:14 Java

来源:cnblogs.com/starluke/p/12558952.htmlKafka是大数据领域无处不在的消息中间件。目前广泛应用于企业内部的实时数据流水线,帮助企业构建自己的流计算应用。Kafka虽然是基于磁盘的数据存储,但具有高性能、高吞吐、低延迟等特点。它的吞吐量可以从几万到几千万不等。但是很多用过Kafka的人经常会被问到这样一个问题,为什么Kafka速度快,吞吐量大;大部分被问到的人一下子就懵了,或者只知道一些简单的点,这篇文章只是简单介绍一下为什么Kafka吞吐量大速度快。另外最近面试整理了Java最新最全的面试题:https://www.javastack.cn/mst/1.顺序读写众所周知,Kafka将消息记录持久化到本地磁盘。大多数人认为磁盘读写性能不佳可能会质疑Kafka性能是如何保证的。其实不管是内存还是磁盘,快慢的关键在于寻址方式。磁盘分为顺序读写和随机读写,内存也分为顺序读写和随机读写。基于磁盘的随机读写确实很慢,但是磁盘的顺序读写性能非常高,一般来说比磁盘随机读写高三个数量级,在某些情况下,磁盘顺序读并且写入性能甚至高于内存随机读写。这里有一张著名学术期刊ACMQueue上的性能对比图:磁盘的顺序读写是磁盘使用模式中最有规律的,操作系统也针对这种模式做了很多优化。Kafka使用磁盘的顺序读写来提高性能。Kafka的消息是连续追加到本地磁盘文件的末尾,而不是随机写入,显着提高了Kafka的写入吞吐量。上图展示了Kafka是如何写入数据的。每个Partition其实就是一个文件。Kafka收到消息后,会在文件末尾(虚帧部分)插入数据。这种方式有一个缺陷——没有办法删除数据,所以Kafka不会删除数据,它会保留所有的数据,每个消费者(Consumer)对于每个Topic都有一个偏移量,代表读取了多少条数据.两个消费者,Consumer1有两个offset分别对应Partition0和Partition1(假设每个topic都有一个Partition);Consumer2有一个offset对应Partition2。这个offset是客户端SDK保存的,Kafka的Broker完全忽略这个东西的存在;一般情况下,SDK会保存在zookeeper中。(所以需要向Consumer提供zookeeper的地址)。如果不删除硬盘,它就会被填满,所以Kakfa提供了两种删除数据的策略。一种是基于时间,另一种是基于分区文件大小。具体配置请参考其配置文档。2.PageCache为了优化读写性能,Kafka使用了操作系统本身的PageCache,也就是使用操作系统本身的内存,而不是JVM空间内存。这样做的好处是:避免对象消耗:如果使用Java堆,Java对象的内存消耗比较大,通常是存储数据的两倍或更多。避免GC问题:随着JVM中数据的不断增加,垃圾回收会变得复杂和缓慢,使用系统缓存时不会出现GC问题。与使用JVM或内存缓存等数据结构相比,使用操作系统Cache的Page更简单可靠。首先,操作系统级别的缓存利用率会更高,因为存储的是紧凑的字节结构而不是单个对象。其次,操作系统本身也对PageCache做了很多优化,提供了write-behind、read-ahead、flush等多种机制。而且,即使服务进程重启,系统缓存也不会消失,避免了进程内缓存重建缓存的过程。通过操作系统的PageCache,Kafka的读写操作基本都基于内存,读写速度有了很大的提升。3、零拷贝linux操作系统的“零拷贝”机制使用了sendfile方法,让操作系统直接从PageCache发送数据到网络,只需要copy操作的最后一步copy数据到NIC缓冲区,从而避免重新复制数据。示意图如下:通过这种“零拷贝”机制,PageCache结合了sendfile方式,Kafka消费端的性能也得到了很大的提升。这就是为什么有时候消费者在持续消费数据的时候,我们并没有看到比较高的磁盘io。此时,提供数据的是操作系统缓存。Kafka客户端从服务端读取数据时,如果不使用零拷贝技术,一般需要经过这样一个过程:操作系统将数据从磁盘读取到内核空间的读缓冲区中。应用程序(即Kafka)将数据从内核空间的读缓冲区复制到用户空间的缓冲区。应用程序将数据从用户空间的缓冲区写回内核空间的套接字缓冲区。操作系统将socket缓冲区中的数据复制到网卡缓冲区中,然后通过网络发送给客户端。从图中可以看出,数据在内核空间和用户空间之间穿梭了两次,那么这个冗余过程是否可以避免呢?当然,Kafka使用了零拷贝技术,即直接将数据从内核空间的readbuffer拷贝到内核空间的socketbuffer,再写入到NICbuffer,避免了数据之间需要拷贝数据内核空间和用户空间。间穿梭。可见,这里的零拷贝并不是说完全没有拷贝,而是为了避免内核空间和用户空间之间的拷贝。如果根本没有副本,那么发送给客户端的数据就没有了,对吧?但是,仅仅省去这一步就可以带来巨大的性能提升。4.Partitionandsegment+indexKafka的消息是按照topic分类存储的,topic中的数据按照partition一个一个的存储在不同的broker节点中。每个分区对应操作系统上的一个文件夹,分区实际上是分段存储的。这也很符合分布式系统中partitioning和bucketing的设计思想。通过这种partition和segment的设计,Kafka的消息实际上是分散存储在一个小的segment中,每次文件操作也是直接操作一个segment。为了进一步优化查询,Kafka默认为分片后的数据文件创建一个索引文件,即文件系统上的.index文件。这种分区和段+索引的设计,不仅提高了数据读取的效率,也提高了数据操作的并行性。5、批量读写Kafka数据的读写也是分批进行,而不是单件进行。除了利用底层技术,Kafka还提供了一些在应用层提升性能的手段。最明显的是批处理的使用。在向Kafka写入数据时,可以开启批量写入,可以避免网络上频繁传输单个消息带来的延迟和带宽开销。假设网络带宽为10MB/S,那么一次传输10MB的消息显然比传输1KB的消息1亿次要快得多。6、批量压缩很多时候,系统的瓶颈不是CPU或者磁盘,而是网络IO,尤其是对于需要在广域网上的数据中心之间发送消息的数据管道。数据压缩对CPU资源的消耗很小,但是对于Kafka来说,网络IO应该更多的考虑。如果对每条消息都进行压缩,但是压缩率比较低,那么Kafka采用的是批量压缩,即多条消息一起压缩,而不是单条消息压缩。Kafka允许使用递归的消息集合,批量消息可以以压缩形式传输,并且可以将压缩格式保存在日志中,直到被消费者解压。Kafka支持多种压缩协议,包括Gzip和Snappy压缩协议。Kafka速度的秘密在于它把所有的消息都变成一个批处理文件,并进行合理的处理。批量压缩减少网络IO损失,通过mmap提高I/O速度。写数据时,最后加一个Partion,速度最优;读取数据时配合sendfile直接暴力输出。近期热点文章推荐:1.1000+Java面试题及答案(2022最新版)2.厉害了!Java协程来了。..3.SpringBoot2.x教程,太全面了!4.不要用爆破爆满画面,试试装饰者模式,这才是优雅的方式!!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!