下面从数据写入和读取两方面分析,为什么Kafka速度这么快。
一、写入数据
1. 利用 Partition 实现并行处理
我们都知道 Kafka 是一个 Pub-Sub 的消息系统,无论是发布还是订阅,都要指定 Topic。
Topic 只是一个逻辑的概念。每个 Topic 都包含一个或多个 Partition,不同 Partition 可位于不同节点。
一方面,由于不同 Partition 可位于不同机器,因此可以充分利用集群优势,实现机器间的并行处理。另一方面,由于 Partition 在物理上对应一个文件夹,即使多个 Partition 位于同一个节点,也可通过配置让同一节点上的不同 Partition 置于不同的磁盘上,从而实现磁盘间的并行处理,充分发挥多磁盘的优势。
能并行处理,速度肯定会有提升,多个工人肯定比一个工人干的快。
2. 顺序写磁盘(避免了随机IO)

图片来源:kafka.apache.org
Kafka 中每个分区是一个有序的,不可变的消息序列,新的消息不断追加到 partition 的末尾,这个就是顺序写。
每个消费者对每个topic都有一个offset用来表示读取到了第几条记录。
通过客户端提交offset的方式来记录 每个消费者读到哪条记录了,Kafka的Broker完全无视这个东西的存在;一般情况下SDK会把它保存到Zookeeper里面,所以需要给Consumer提供zookeeper的地址。
如果不删除硬盘肯定会被撑满,所以Kakfa提供了两种策略来删除数据:
- 一是基于时间;
- 二是基于partition文件大小。
先介绍一下零拷贝技术
零拷贝技术指在计算机执行操作时,CPU不需要先将数据从一个内存区域复制到另一个内存区域,从而可以减少上小文切换以及CPU拷贝事件。它的作用是在数据报从网络设备到用户程序传递的过程中,减少数据拷贝次数,减少系统调用,实现CPU的零参与,彻底消除CPU在这方面的负载 目前零拷贝技术主要有三种类型:
直接I/O:数据直接跨过内核,在用户地址空间与I/O设备之间传递,内核只是进行必要的虚拟存储配置等辅助工作;
避免内核和用户空间之间的数据拷贝:当应用程序不需要对数据进行访问时,则可以避免将数据从内核空间拷贝到用户空间
- mmap
- sendfile
- splice && tee
- sockmap
copy on write:写时拷贝技术,数据不需要提前拷贝,而是当需要修改的时候再进行部分拷贝。
3.利用零拷贝技术之Memory Mapped Files (网络数据持久化到磁盘 (Producer 到 Broker))
传统模式下,数据从网络传输到文件需要 4 次数据拷贝、4 次上下文切换和两次系统调用。
data = socket.read()// 读取网络数据
File file = new File()
file.write(data)// 持久化到磁盘
file.flush()
这一过程实际上发生了四次数据拷贝:
- 首先通过 DMA copy 将网络数据拷贝到内核态 Socket Buffer
- 然后应用程序将内核态 Buffer 数据读入用户态(CPU copy)
- 接着用户程序将用户态 Buffer 再拷贝到内核态(CPU copy)
- 最后通过 DMA copy 将数据拷贝到磁盘文件
DMA(Direct Memory Access):直接存储器访问。DMA 是一种无需 CPU 的参与,让外设和系统内存之间进行双向数据传输的硬件机制。使用 DMA 可以使系统 CPU 从实际的 I/O 数据传输过程中摆脱出来,从而大大提高系统的吞吐率。
同时,还伴随着四次上下文切换,如下图所示

Memory Mapped Files(后面简称mmap)也被翻译成 内存映射文件
使用 mmap 的目的是将内核中读缓冲区(read buffer)的地址与用户空间的缓冲区(user buffer)进行映射。从而实现内核缓冲区与应用程序内存的共享,省去了将数据从内核读缓冲区(read buffer)拷贝到用户缓冲区(user buffer)的过程。
它的工作原理是直接利用操作系统的 Page 来实现文件到物理内存的直接映射。完成映射之后你对物理内存的操作会被同步到硬盘上。使用这种方式可以获取很大的 I/O 提升,省去了用户空间到内核空间复制的开销。
对于 kafka 来说,Producer 生产的数据存到 broker,这个过程读取到 socket buffer 的网络数据,其实可以直接在内核空间完成落盘。并没有必要将 socket buffer 的网络数据,读取到应用进程缓冲区;在这里应用进程缓冲区其实就是 broker,broker 收到生产者的数据,就是为了持久化。
使用这种方式可以获取很大的I/O提升,省去了用户空间到内核空间复制的开销(调用文件的read会把数据先放到内核空间的内存中,然后再复制到用户空间的内存中。)
但也有一个很明显的缺陷——不可靠,写到mmap中的数据并没有被真正的写到硬盘,操作系统会在程序主动调用flush的时候才把数据真正的写到硬盘。
Kafka提供了一个参数——producer.type来控制是不是主动flush,如果Kafka写入到mmap之后就立即flush然后再返回Producer叫 同步 (sync);写入mmap之后立即返回Producer不调用flush叫异步 (async)。

二、读取数据(磁盘文件通过网络发送(Broker 到 Consumer)
1.通过senfile实现零拷贝
Kafka在读取磁盘时做了哪些优化?
传统方式实现:先读取磁盘、再用 socket 发送,实际也是进过四次 copy
buffer = File.read
Socket.send(buffer)
这一过程可以类比上边的生产消息:
- 首先通过系统调用将文件数据读入到内核态 Buffer(DMA 拷贝)
- 然后应用程序将内存态 Buffer 数据读入到用户态 Buffer(CPU 拷贝)
- 接着用户程序通过 Socket 发送数据时将用户态 Buffer 数据拷贝到内核态 Buffer(CPU 拷贝)
- 最后通过 DMA 拷贝将数据拷贝到 NIC Buffer(网卡缓冲区)
Linux 2.4+ 内核通过 sendfile 系统调用,提供了零拷贝。数据通过 DMA 拷贝到内核态 Buffer 后,直接通过 DMA 拷贝到 NIC Buffer,无需 CPU 拷贝。这也是零拷贝这一说法的来源。除了减少数据拷贝外,因为整个读文件 - 网络发送由一个 sendfile 调用完成,整个过程只有两次上下文切换,因此大大提高了性能。

三、数据压缩和批处理
在很多情况下,系统的瓶颈不是 CPU 或磁盘,而是网络IO。
因此,除了操作系统提供的低级批处理之外,Kafka 的客户端和 broker 还会在通过网络发送数据之前,在一个批处理中累积多条记录 (包括读和写)。记录的批处理分摊了网络往返的开销,使用了更大的数据包从而提高了带宽利用率。
Producer 可将数据压缩后发送给 broker,从而减少网络传输代价,目前支持的压缩算法有:Snappy、Gzip、LZ4。数据压缩一般都是和批处理配套使用来作为优化手段的。
总结:
kafka为何这么快可以从三方面入手
一方面是写入数据,也就是数据从Producer发到Broker保存起来
该方面的优化包括:
- 1.利用partition实现并行处理 ,由于不同 Partition 可位于不同机器,因此可以充分利用集群优势,实现机器间的并行处理
- 2.利用顺序写磁盘,避免了随机IO
- 3.利用零拷贝技术中的Memory Mapped Files ,谜底是将内核空间缓冲区和用户进程缓冲区,进行映射,这样省去了数据在内核空间缓冲区到用户进程缓冲区之间的复制
第二个方面是:读取数据,也就是数据从Broker到Consumer
该方面的优化包括:
- 通过sendfile来实现零拷贝 ,数据通过DMA从硬盘中拷贝到内核态缓冲区之后,直接DMA拷贝到NIC Buffer
第三个方面:网络传输方面
该方面的优化包括:
- 记录的批处理分摊了网络往返的开销,使用了更大的数据包从而提高了带宽利用率,同时压缩后减少了传输到的代价
第四个方面:也就是Kafka是如何处理请求的
详见:
参考:
https://mp.weixin.qq.com/s/dbnpPEF0FBB5A5xH21OoeQ

1678

被折叠的 条评论
为什么被折叠?



