? ? ? ? Kafka在性能方面有着显著的优势,这也使得Kafka的应用非常广泛,那kakfa的性能为何如此优异呢?本文将带你探寻kafka高性能之谜。
? ? ? ? kafka的高性能概括起来有如下几点:顺序写入磁盘与I/O优化、批量处理、页缓存、零拷贝技术、分区并行处理、高效的消息压缩。下面将分别极少这几方面的内容。
????????人们对于磁盘速度慢的普遍印象,使得人们对于持久化的架构提供强有力的性能产生怀疑。事实上,磁盘的速度快慢取决于使用磁盘的方式,而且设计合理的磁盘结构通常可以和网络一样快。
????????关于磁盘性能的关键事实是,磁盘的吞吐量和过去十年里磁盘的寻址不同。使用6个7200rpm、SATA接口、RAID-5的磁盘阵列在JBOD配置下的顺序写入的性能约为600MB/s,但随机写入的性能仅约为100k/s,相差6000倍以上。因为线性的读取和写入是磁盘使用模式中最有规律的,并且由操作系统进行大量的优化,现代操作系统提供了read-ahead和write-behind技术。
????????read-ahead:操作系统文件预读,linux系统内核将指定文件的某区域预读进页缓存起来,便于对该区域进行读取时,不会因缺页而阻塞。预读可以有效地减少磁盘的寻址次数和应用程序的I/O等待时间。
????????write-behind:将多个小型的逻辑合并成一次大型的物理磁盘写入。
? ? ? ? 一个主题对应多个分区,每个分区又有多个副本,一个副本对应一个日志(Log),为了防止Log过大,Kafka又引入了日志分段(LogSegment),将log切分成多个LogSegmnt,相当于一个巨型文件被平均分成多个相对较小的文件,这样便于消息的维护和清理。
????????事实上,Log和LogSegment也不是纯粹物理意义上的概念,Log在物理上只以文件夹的形式存储,而每个LogSegment对应于磁盘上一个日志文件和两个索引文件,以及可能的其他文件。下图描述了主题、分区与副本、日志之间的关系。
????????在Log中追加消息时是?顺序写入?的,只有最后一个LogSegment才能执行写入操作,在此之前所有的LogSegment都不能写入数据。日志中还存在索引文件,可以高效的定位消息,提高效率。
? ? ? ? 从这里就能看出,日志的内容是顺序写入的,所以磁盘的性能比随机写入要好的多,这是kafka高性能的第一个原因。
? ? ? ? 批量处理提高性能很好理解,将多次操作合并为一次操作.
????????生产者发送消息时支持批量发送消息,将多条消息积累到一个批次中,然后一次性发送给Kafka Broker,这样可以减少网络I/O次数,显著提升吞吐量,并降低延迟,尤其是在网络带宽成为瓶颈时。
????????在消费者端,Kafka也支持批处理的机制。消费者可以在处理完一批消息后,一起提交这些消息的偏移量(Offset),而不是每次消费一条消息就立即提交Offset。这种批量提交策略有助于减少与Kafka Broker之间的通信开销,同时也能保证了整体消费进度的一致性。
? ? ? ? 在broker端,可以通过write-behind等技术,将多次磁盘操作合并为一个,减少刷盘操作。
? ? ? ? 这是Kafka高性能的第二个原因。
????????页缓存是操作系统实现的一种主要的磁盘缓存,以此用来减少对磁盘I/O的操作。具体来说就是把磁盘中的数据缓存到内存中,把对磁盘的访问变为对内存的访问。
????????当一个进程准备读取磁盘上的文件时,操作系统会先查看读取的数据所在的页(page)是否在页缓存(pagecache)中,如果存在则直接返回数据,从而避免了对物理磁盘的I/O操作;如果没有命中,则操作系统会像磁盘发起读取请求并将读取的数据页存入页缓存,之后再将数据返回给进程。同样一个进程需要将数据写入磁盘,那么操作系统也会检测数据对应的页是否在页缓存中,如果不存在则会先在页缓存中添加相应的页,最后将数据写入对应的页。被修改的页也就变成了脏页,操作系统会在合适的时间把脏页写入磁盘,以保持数据的一致性。
????????Linux操作系统中vm.dirty_background_ratio参数用来指定当脏页数量达到系统内存的百分之多少后会触发pdflush/flush/kdmflush等后台回写进程的运行来处理脏页。
????????kafka中大量的使用了页缓存,这也是kafka实现高吞吐量的重要因素之一。虽然消息都是先被写入页缓存,然后由操作系统负责具体的刷盘任务,但在kafka中同样提供了同步刷盘即阶段性强制刷盘(fsyc)的功能。
? ? ? ? 这是Kafka高性能的第三个原因。但有一种情况需要引起注意,会导致页缓存失效。在kafka出现严重挤压时,生产者开始时的确是把消息写入到了页缓存,但由于消费者处理速度过慢,挤压严重,那页缓存中的数据会不断的被置换到磁盘上,当消费者消费消息时,可能需要从磁盘读取了,这对性能是极大的损耗,所以要尽量避免kafka出现严重的积压情况。
????????磁盘可以说是计算机系统最慢的硬件之一,读写速度相差内存10倍以上,所以针对磁盘的优化技术非常多,比如零拷贝、直接I/O、异步I/O等等,这些优化的目的是为了提高系统的吞吐量,这里主要介绍零拷贝。
????????我们以文件传输作为切入点,来分析I/O工作方式,以及如何优化传输文件的性能。
????????在没有DMA技术前,I/O是这样工作的:????????? ? ? ?
????????
????????可以看到整个数据传输过程,都需要CPU亲自参与搬运数据的过程,而且这个过程,CPU是不能做其他事情的。简单的搬运几个字符数据是没问题的,但是如果我们用千兆网卡或者硬盘传输大量数据的时候,都用CPU来搬运的话,肯定忙不过来。
????????于是DMA技术就诞生了,也就是直接内存访问(Direct Memory Acess)技术。简单理解就是,DMA方式无需处理器的干预,在内存与I/O之间进行快速数据交换。在进行I/O设备的内存的数据传输的时候,数据搬运工作全部交给DMA控制器,而CPU不在参与任何数据搬运相关的事情,这样CPU就可以去处理别的事情。
????????可以看到,整个数据传输过程,CPU不在参与数据搬运的工作,而是全程由DMA完成,但是CPU在这个过程中也是必不可少的,因为传输什么数据,从哪里传输到哪里,都需要CPU来告诉DMA控制器。
????????如果服务端要提供文件传输功能,我们能想到的最简单的方式:将磁盘上的文件读取出来,然后通过网络协议发送给客户端。
????????传统I/O的工作方式是,数据读取和写入是从用户空间到内核空间来回复制,内核空间的数据是通过操作系统层面的I/O接口从磁盘读取或写入的。
????????首先,期间共发生了4次用户态与内核态的上下文切换,因为发生了两次系统调用,一次是read,一次是write,每次系统调用都得先从用户态切换到内核态,等内核完成任务后,再从内核态切换会用户态。
????????上线文切换成本并不小,一次切换需要耗时几十纳秒到几微妙,虽然看上去时间很短,但是在高并发的场景下,这类时间容易被累计放大,从而影响系统整体性能。
????????其次,发生了四次数据拷贝,其中两次是DMA的拷贝,另外两次则是通过CPU拷贝的:
????????看这个文件的传输过程,我们只是搬运一份数据,结果却搬运了4次,过多的数据拷贝无疑会消耗CPU资源,大大降低系统性能。
????????这种简单又传统的方式,存在冗余的上下文切换和数据拷贝,在高并发系统里是非常糟糕的,多了很多不必要的开销,会严重影响系统性能。所以要想提高文件传输的性能,就要减少用户态与内核态的上下问切换与内存拷贝的次数。
????????Linux 内核从 2.4 版本开始,对于网卡支持SG-DMA技术的情况下,sendFile()系统调用的过程发生了变化。
????????就是所谓的零拷贝技术,因为我们没有在内存层面去拷贝数据,也就是全程没有通过CPU来搬运数据,所有的数据都通过DMA来进行传输。与传统方式相比,减少了两次上下文切换和数据拷贝次数,只需要两次上下文切换就可以完成文件传输,而且两次数据拷贝都不要CPU的参与,所以,零拷贝技术可以把文件传输的性能提高至少一倍以上。
? ? ? ? kafka在数据传输中尽可能减少数据复制环节,利用操作系统提供的零拷贝技术,将数据直接从文件缓存区传递到网络协议栈,降低了CPU开销和延迟。这是kafka高性能的第四个原因。
? ? ? ? 这个很好理解,Kafka将主题拆分为多个分区,并且可以在不同的broker节点上分布这些分区,实现了并行处理能力,从而能够扩展以应对高并发场景下的读写需求。
? ? ? ??这是kafka高性能的第五个原因。
????????支持消息压缩功能,在不影响消息传递速度的同时降低网络带宽消耗,进一步提升整体性能。
????????常见的压缩算法是数据量越大压缩效果越好,一条消息通常不会太大,这就导致压缩效果不太好。而Kafka实现的压缩方式是将多条消息一起进行压缩,这样可以保证比较好的压缩效果。????????
????????producer将消息进行压缩,发往broker,broker保存消息。这里需要注意,producer和broker的压缩算法要一致,broker端有参数可以配置压缩算法,默认值是producer即保持生产者的压缩算法。如果两端的压缩算法不一致,producer用一种压缩算法,发到broker后 broker需要对压缩消息进行解压,解压后在按照broker设置的压缩算法进行压缩,然后在保存,多了许多额外的操作,会导致broker的CPU变高,导致吞吐量下降,效率降低。消费者与broker有同样的问题。常用的压缩算法有GZIO、SNAPPY、LZ4。
? ? ? ? 将多条消息进行压缩处理,这是kafka高性能的第六个原因。
? ? ? ? 关于kafka高性能的原因就介绍到这里,不知道你学会了吗,欢迎留言讨论。