APP下载

面试中考官会问:Kafka为什么速度那么快?

消息来源:baojiabao.com 作者: 发布时间:2024-05-02

报价宝综合消息面试中考官会问:Kafka为什么速度那么快?

Kafka的讯息是储存或快取在磁盘上的,一般认为在磁盘上读写资料是会降低效能的,因为定址会比较消耗时间,但是实际上,Kafka的特性之一就是高吞吐率。即使是普通的服务器,Kafka也可以轻松支援每秒百万级的写入请求,超过了大部分的讯息中介软件,这种特性也使得Kafka在日志处理等海量资料场景广泛应用。

针对Kafka的基准测试可以参考,Apache Kafka基准测试:每秒写入2百万(在三台廉价机器上)

下面从资料写入和读取两方面分析,为什么Kafka速度这么快。

一、写入资料

Kafka会把收到的讯息都写入到硬盘中,它绝对不会丢失资料。为了优化写入速度Kafka采用了两个技术, 顺序写入和MMFile 。

顺序写入

磁盘读写的快慢取决于你怎么使用它,也就是顺序读写或者随机读写。在顺序读写的情况下,磁盘的顺序读写速度和内存持平。

因为硬盘是机械结构,每次读写都会定址->写入,其中定址是一个“机械动作”,它是最耗时的。所以硬盘最讨厌随机I/O,最喜欢顺序I/O。为了提高读写硬盘的速度,Kafka就是使用顺序I/O。

而且Linux对于磁盘的读写优化也比较多,包括read-ahead和write-behind,磁盘快取等。如果在内存做这些操作的时候,一个是JAVA物件的内存开销很大,另一个是随着堆内存资料的增多,JAVA的GC时间会变得很长,使用磁盘操作有以下几个好处:

1、顺序写入磁盘顺序读写速度超过内存随机读写

2、顺序写入JVM的GC效率低,内存占用大。使用磁盘可以避免这一问题

3、顺序写入系统冷启动后,磁盘快取依然可用

下图就展示了Kafka是如何写入资料的, 每一个Partition其实都是一个档案 ,收到讯息后Kafka会把资料插入到档案末尾(虚框部分):

这种方法有一个缺陷——没有办法删除资料 ,所以Kafka是不会删除资料的,它会把所有的资料都保留下来,每个消费者(Consumer)对每个Topic都有一个offset用来表示读取到了第几条资料 。

两个消费者:

1、顺序写入Consumer1有两个offset分别对应Partition0、Partition1(假设每一个Topic一个Partition);

2、顺序写入Consumer2有一个offset对应Partition2。

这个offset是由客户端SDK负责储存的,Kafka的Broker完全无视这个东西的存在;一般情况下SDK会把它储存到Zookeeper里面,所以需要给Consumer提供zookeeper的地址。

如果不删除硬盘肯定会被撑满,所以Kakfa提供了两种策略来删除资料:

1、顺序写入一是基于时间。

2、顺序写入二是基于partition档案大小。

具体配置可以参看它的配置文件

Memory Mapped Files

即便是顺序写入硬盘,硬盘的访问速度还是不可能追上内存。所以Kafka的资料并不是实时的写入硬盘 ,它充分利用了现代操作系统分页储存来利用内存提高I/O效率。

Memory Mapped Files(后面简称mmap)也被翻译成 内存对映档案 ,在64位操作系统中一般可以表示20G的资料档案,它的工作原理是直接利用操作系统的Page来实现档案到实体内存的直接对映。

完成对映之后你对实体内存的操作会被同步到硬盘上(操作系统在适当的时候)。

通过mmap,程序像读写硬盘一样读写内存(当然是虚拟机器内存),也不必关心内存的大小有虚拟内存为我们兜底。

使用这种方式可以获取很大的I/O提升,省去了使用者空间到核心空间复制的开销(呼叫档案的read会把资料先放到核心空间的内存中,然后再复制到使用者空间的内存中。)

但也有一个很明显的缺陷——不可靠,写到mmap中的资料并没有被真正的写到硬盘,操作系统会在程式主动呼叫flush的时候才把资料真正的写到硬盘。

Kafka提供了一个引数——producer.type来控制是不是主动flush,如果Kafka写入到mmap之后就立即flush然后再返回Producer叫 同步 (sync);写入mmap之后立即返回Producer不呼叫flush叫异步 (async)。

二、读取资料

Kafka在读取磁盘时做了哪些优化?

基于sendfile实现Zero Copy

传统模式下,当需要对一个档案进行传输的时候,其具体流程细节如下:

1、基于sendfile实现Zero Copy呼叫read函式,档案资料被copy到核心缓冲区

2、read函式返回,档案资料从核心缓冲区copy到使用者缓冲区

3、write函式呼叫,将档案资料从使用者缓冲区copy到核心与socket相关的缓冲区。

4、资料从socket缓冲区copy到相关协议引擎。

以上细节是传统read/write方式进行网络档案传输的方式,我们可以看到,在这个过程当中,档案资料实际上是经过了四次copy操作:

硬盘—>核心buf—>使用者buf—>socket相关缓冲区—>协议引擎

而sendfile系统呼叫则提供了一种减少以上多次copy,提升档案传输效能的方法。

在核心版本2.1中,引入了sendfile系统呼叫,以简化网络上和两个本地档案之间的资料传输。sendfile的引入不仅减少了资料复制,还减少了上下文切换。

sendfile(socket, file, len);

执行流程如下:

1、sendfile系统呼叫,档案资料被copy至核心缓冲区

2、再从核心缓冲区copy至核心中socket相关的缓冲区

3、最后再socket相关的缓冲区copy到协议引擎

相较传统read/write方式,2.1版本核心引进的sendfile已经减少了核心缓冲区到user缓冲区,再由user缓冲区到socket相关缓冲区的档案copy,而在核心版本2.4之后,档案描述符结果被改变,sendfile实现了更简单的方式,再次减少了一次copy操作。

在Apache、Nginx、lighttpd等web服务器当中,都有一项sendfile相关的配置,使用sendfile可以大幅提升档案传输效能。

Kafka把所有的讯息都存放在一个一个的档案中,当消费者需要资料的时候Kafka直接把档案传送给消费者,配合mmap作为档案读写方式,直接把它传给sendfile。

批量压缩

在很多情况下,系统的瓶颈不是CPU或磁盘,而是网络IO,对于需要在广域网上的资料中心之间传送讯息的资料流水线尤其如此。进行资料压缩会消耗少量的CPU资源,不过对于kafka而言,网络IO更应该需要考虑。

1、如果每个讯息都压缩,但是压缩率相对很低,所以Kafka使用了批量压缩,即将多个讯息一起压缩而不是单个讯息压缩

2、Kafka允许使用递回的讯息集合,批量的讯息可以通过压缩的形式传输并且在日志中也可以保持压缩格式,直到被消费者解压缩

3、Kafka支援多种压缩协议,包括Gzip和Snappy压缩协议

三、总结

Kafka速度的秘诀在于,它把所有的讯息都变成一个批量的档案,并且进行合理的批量压缩,减少网络IO损耗,通过mmap提高I/O速度,写入资料的时候由于单个Partion是末尾新增所以速度最优;读取资料的时候配合sendfile直接暴力输出。

对Java技术,架构技术感兴趣的同学,欢迎加QQ群728821520,一起学习,相互讨论。

2020-01-02 06:49:00

相关文章