kafka——高性能篇

引言

kafka是一个高吞吐量的分布式消息系统,它可以支持处理实时数据,提供实时数据。kafka的设计之初就是注重吞吐量的提升,它的高性能,高可吞吐量以从这么以下几点去讨论:
分区
网络传输上减少开销
顺序读写
零拷贝技术
优秀的文件存储机制

分区

kafka是个分布式集群的系统,整个系统可以包含多个broker,也就是多个服务器实例。每个主题topic会有多个分区,kafka将分区均匀地分配到整个集群中,当生产者向对应主题传递消息,消息通过负载均衡机制传递到不同的分区以减轻单个服务器实例的压力。如下图所示:
kafka——高性能篇
一个Consumer Group中可以有多个consumer,多个consumer可以同时消费不同分区的消息,大大的提高了消费者的并行消费能力。但是一个分区中的消息只能被一个Consumer Group中的一个consumer消费。

网络传输上减少开销

批量发送
在发送消息的时候,kafka不会直接将少量数据发送出去,否则每次发送少量的数据会增加网络传输频率,降低网络传输效率。kafka会先将消息缓存在内存中,当超过一个的大小或者超过一定的时间,那么会将这些消息进行批量发送。
端到端压缩
当然网络传输时数据量小也可以减小网络负载,kafaka会将这些批量的数据进行压缩,将一批消息打包后进行压缩,发送broker服务器后,最终这些数据还是提供给消费者用,所以数据在服务器上还是保持压缩状态,不会进行解压,而且频繁的压缩和解压也会降低性能,最终还是以压缩的方式传递到消费者的手上。

顺序读写

kafka是个可持久化的日志服务,它将数据以数据日志的形式进行追加,最后持久化在磁盘中。kafka消息存储时依赖于文件系统,我们普遍认为磁盘的性能比不上内存性能,但是kafka却将磁盘性能发挥得淋漓尽致。在一个由6个7200rpm的SATA硬盘组成的RAID-5磁盘阵列上,线性写入(linear write)的速度大约是300MB/秒,但随即写入却只有50k/秒。可见磁盘的线性和随机读写的速度差距甚大。为了利用数据的局部相关性,操作系统从磁盘中读取数据以数据块为单位,将一个数据块读入内存中,如果有相邻的数据,就不用再去磁盘中读取。在某些情况下,顺序磁盘访问能比随机内存访问还要快。同时在写数据的时候也是将一整块数据块写入磁盘中,大大提升了IO效率。
现代操作系统乐于将更多的空闲内存来当作磁盘缓存。当我们在程序中对数据进行缓存时,可能这些数据已经缓存在了操作系统的缓存页中。我们将缓存的操作逻辑交给操作系统,那么比我们自己维护来得更加高效。所以使用磁盘的方式进行线性地读取数据也有很高的效率。
kafka将消息追加到日志文件中,正是利用了磁盘的顺序读写,来提高读写效率。我们平时操作磁盘可能会用Btree这种数据结构,但是运算的时间复杂度为O(logN),持久化队列利用追加日志的方式构建,生产者将消息追加到日志尾部,消费者读取头部的消息,两者互不干扰,也不需要加锁,提高了性能,同时时间复杂度为O(1)。

零拷贝

kafka将数据以日志的形式保存在磁盘中。当消费者向服务器请求数据,那么需要从文件传输到socket中。
那么从文件到socker需要以下这些步骤:
①调用read陷入内核模式,操作系统将数据从磁盘读到内核缓冲区;
②然后从内核态切换到用户态,应用程序将数据从内核空间读取到用户空间的缓冲区;
③然后应用程序将数据写带内核空间的socket缓冲区;
④最后操作系统将socket缓冲区的数据拷贝到网卡接口缓冲区并发出去。
从上面可见,当我们将数据从文件传输到socket最后发送出去经过了好几次拷贝,同时还有好几次的用户态和内核态的切换,我们知道用户态和内核态的切换也是很耗时的,那么多次拷贝对性能的影响更是雪上加霜。
从上面的过程来看,可以看出没必要从内核空间的缓冲区拷贝到用户空间。所以零拷贝技术正是改进了这项确定,零拷贝将文件内容从磁盘通过DMA引擎复制到内核缓冲区,而且没有把数据复制到socket缓冲区,只是将数据位置和长度信息的描述符复制到了socket缓存区,然后直接将数据传输到网络接口,最后发送。这样大大减小了拷贝的次数,提高了效率。kafka正是调用linux系统给出的sendfile系统调用来使用零拷贝。Java中的系统调用给出的是FileChannel.transferTo接口。

优秀的文件存储机制

之前说过一个主题可以有多个分区,假设只有一个服务器broker,那么多个分区必然是存在一个服务器上。kafka将一个分区以一个目录的方式存储,目录的命名为topicname-分区下标,例如有个topic叫做hello,有3个分区,那么就有三个文件夹分别为hello-0,hello-1,hello-2。在一个分区文件夹中,又分为多个段文件。段文件又由一个index索引文件和一个log实质的数据日志文件构成。文件的命名规则为日志文件中第一个消息的offset值-1,offset可以理解为消息id,例如一个000….0015354.log这个文件中消息最小的offset为15353。
kafka——高性能篇
log数据文件有消息和偏移量构成,而索引文件中的索引用的是稀疏索引。稀疏索引减少的索引文件的大小。索引文件中存着消息的物理偏移量。
当我们从对应分区中查找对应消息的时候,先将分区下的文件列表根据命名进行排序,通过二分查找,找到对应offset所对用的段文件,然后根据索引文件中的索引找到对应的物理偏移地址,比如我们找message4.那么找到的物理偏移地址为4597,然后从4597的偏移地址通过顺序查找到message4。
由上可见对应消息的查找速度是非常之快的。

ps:参考博客:https://blog.csdn.net/derekjiang/article/details/9053863