Kafka 为什么能那么快 | Kafka高效读写数据的起因
无论 kafka 作为 MQ 也好,作为存储层也罢,无非就是两个性能(好简略的样子),一是 Producer 生产的数据存到 broker,二是 Consumer 从 broker 读取数据。那 Kafka 的快也就体现在读写两个方面了,上面咱们就聊聊 Kafka 快的起因。
1. 利用 Partition 实现并行处理
咱们都晓得 Kafka 是一个 Pub-Sub 的音讯零碎,无论是公布还是订阅,都要指定 Topic。
Topic 只是一个逻辑的概念。每个 Topic 都蕴含一个或多个 Partition,不同 Partition 可位于不同节点。
一方面,因为不同 Partition 可位于不同机器,因而能够充分利用集群劣势,实现机器间的并行处理。另一方面,因为 Partition 在物理上对应一个文件夹,即便多个 Partition 位于同一个节点,也可通过配置让同一节点上的不同 Partition 置于不同的磁盘上,从而实现磁盘间的并行处理,充分发挥多磁盘的劣势。
能并行处理,速度必定会有晋升,多个工人必定比一个工人干的快。
能够并行写入不同的磁盘?那磁盘读写的速度能够管制吗?
那就先简略扯扯磁盘/IO 的那些事
硬盘性能的制约因素是什么?如何依据磁盘I/O个性来进行零碎设计?硬盘外部次要部件为磁盘盘片、传动手臂、读写磁头和主轴马达。理论数据都是写在盘片上,读写次要是通过传动手臂上的读写磁头来实现。理论运行时,主轴让磁盘盘片转动,而后传动手臂可舒展让读取头在盘片上进行读写操作。磁盘物理构造如下图所示:
因为繁多盘片容量无限,个别硬盘都有两张以上的盘片,每个盘片有两面,都可记录信息,所以一张盘片对应着两个磁头。盘片被分为许多扇形的区域,每个区域叫一个扇区。盘片外表上以盘片核心为圆心,不同半径的同心圆称为磁道,不同盘片雷同半径的磁道所组成的圆柱称为柱面。磁道与柱面都是示意不同半径的圆,在许多场合,磁道和柱面能够调换应用。磁盘盘片垂直视角如下图所示:
图片起源:commons.wikimedia.org
影响磁盘的关键因素是磁盘服务工夫,即磁盘实现一个I/O申请所破费的工夫,它由寻道工夫、旋转提早和数据传输工夫三局部形成。
机械硬盘的间断读写性能很好,但随机读写性能很差,这次要是因为磁头挪动到正确的磁道上须要工夫,随机读写时,磁头须要不停的挪动,工夫都节约在了磁头寻址上,所以性能不高。掂量磁盘的重要次要指标是IOPS和吞吐量。
在许多的开源框架如 Kafka、HBase 中,都通过追加写的形式来尽可能的将随机 I/O 转换为程序 I/O,以此来升高寻址工夫和旋转延时,从而最大限度的进步 IOPS。
感兴趣的同学能够看看 磁盘I/O那些事
磁盘读写的快慢取决于你怎么应用它,也就是程序读写或者随机读写。
2. 程序写磁盘
图片起源:kafka.apache.org
Kafka 中每个分区是一个有序的,不可变的音讯序列,新的音讯一直追加到 partition 的开端,这个就是程序写。
很久很久以前就有人做过基准测试:《每秒写入2百万(在三台便宜机器上)》http://ifeve.com/benchmarking...
因为磁盘无限,不可能保留所有数据,实际上作为音讯零碎 Kafka 也没必要保留所有数据,须要删除旧的数据。又因为程序写入的起因,所以 Kafka 采纳各种删除策略删除数据的时候,并非通过应用“读 - 写”模式去批改文件,而是将 Partition 分为多个 Segment,每个 Segment 对应一个物理文件,通过删除整个文件的形式去删除 Partition 内的数据。这种形式革除旧数据的形式,也防止了对文件的随机写操作。
3. 充分利用 Page Cache
引入 Cache 层的目标是为了进步 Linux 操作系统对磁盘拜访的性能。Cache 层在内存中缓存了磁盘上的局部数据。当数据的申请达到时,如果在 Cache 中存在该数据且是最新的,则间接将数据传递给用户程序,罢黜了对底层磁盘的操作,进步了性能。Cache 层也正是磁盘 IOPS 为什么能冲破 200 的次要起因之一。在 Linux 的实现中,文件 Cache 分为两个层面,一是 Page Cache,另一个 Buffer Cache,每一个 Page Cache 蕴含若干 Buffer Cache。Page Cache 次要用来作为文件系统上的文件数据的缓存来用,尤其是针对当过程对文件有 read/write 操作的时候。Buffer Cache 则次要是设计用来在系统对块设施进行读写的时候,对块进行数据缓存的零碎来应用。
应用 Page Cache 的益处:
- I/O Scheduler 会将间断的小块写组装成大块的物理写从而进步性能
- I/O Scheduler 会尝试将一些写操作从新按程序排好,从而缩小磁盘头的挪动工夫
- 充分利用所有闲暇内存(非 JVM 内存)。如果应用应用层 Cache(即 JVM 堆内存),会减少 GC 累赘
- 读操作可间接在 Page Cache 内进行。如果生产和生产速度相当,甚至不须要通过物理磁盘(间接通过 Page Cache)替换数据
- 如果过程重启,JVM 内的 Cache 会生效,但 Page Cache 依然可用
Broker 收到数据后,写磁盘时只是将数据写入 Page Cache,并不保证数据肯定齐全写入磁盘。从这一点看,可能会造成机器宕机时,Page Cache 内的数据未写入磁盘从而造成数据失落。然而这种失落只产生在机器断电等造成操作系统不工作的场景,而这种场景齐全能够由 Kafka 层面的 Replication 机制去解决。如果为了保障这种状况下数据不失落而强制将 Page Cache 中的数据 Flush 到磁盘,反而会升高性能。也正因如此,Kafka 尽管提供了 flush.messages
和 flush.ms
两个参数将 Page Cache 中的数据强制 Flush 到磁盘,然而 Kafka 并不倡议应用。
4. 零拷贝技术
Kafka 中存在大量的网络数据长久化到磁盘(Producer 到 Broker)和磁盘文件通过网络发送(Broker 到 Consumer)的过程。这一过程的性能间接影响 Kafka 的整体吞吐量。
操作系统的外围是内核,独立于一般的应用程序,能够拜访受爱护的内存空间,也有拜访底层硬件设施的权限。为了防止用户过程间接操作内核,保障内核平安,操作系统将虚拟内存划分为两局部,一部分是内核空间(Kernel-space),一部分是用户空间(User-space)。
传统的 Linux 零碎中,规范的 I/O 接口(例如read,write)都是基于数据拷贝操作的,即 I/O 操作会导致数据在内核地址空间的缓冲区和用户地址空间的缓冲区之间进行拷贝,所以规范 I/O 也被称作缓存 I/O。这样做的益处是,如果所申请的数据曾经寄存在内核的高速缓冲存储器中,那么就能够缩小理论的 I/O 操作,但害处就是数据拷贝的过程,会导致 CPU 开销。
咱们把 Kafka 的生产和生产简化成如下两个过程来看:
- 网络数据长久化到磁盘 (Producer 到 Broker)
- 磁盘文件通过网络发送(Broker 到 Consumer)
4.1 网络数据长久化到磁盘 (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 数据传输过程中解脱进去,从而大大提高零碎的吞吐率。
同时,还随同着四次上下文切换,如下图所示
数据落盘通常都是非实时的,kafka 生产者数据长久化也是如此。Kafka 的数据并不是实时的写入硬盘,它充分利用了古代操作系统分页存储来利用内存进步 I/O 效率,就是上一节提到的 Page Cache。
对于 kafka 来说,Producer 生产的数据存到 broker,这个过程读取到 socket buffer 的网络数据,其实能够间接在内核空间实现落盘。并没有必要将 socket buffer 的网络数据,读取到利用过程缓冲区;在这里利用过程缓冲区其实就是 broker,broker 收到生产者的数据,就是为了长久化。
在此非凡场景
下:接管来自 socket buffer 的网络数据,利用过程不须要两头解决、间接进行长久化时。能够应用 mmap 内存文件映射。
Memory Mapped Files:简称 mmap,也有叫 MMFile 的,应用 mmap 的目标是将内核中读缓冲区(read buffer)的地址与用户空间的缓冲区(user buffer)进行映射。从而实现内核缓冲区与应用程序内存的共享,省去了将数据从内核读缓冲区(read buffer)拷贝到用户缓冲区(user buffer)的过程。它的工作原理是间接利用操作系统的 Page 来实现文件到物理内存的间接映射。实现映射之后你对物理内存的操作会被同步到硬盘上。应用这种形式能够获取很大的 I/O 晋升,省去了用户空间到内核空间复制的开销。
mmap 也有一个很显著的缺点——不牢靠,写到 mmap 中的数据并没有被真正的写到硬盘,操作系统会在程序被动调用 flush 的时候才把数据真正的写到硬盘。Kafka 提供了一个参数——producer.type
来管制是不是被动flush;如果 Kafka 写入到 mmap 之后就立刻 flush 而后再返回 Producer 叫同步(sync);写入 mmap 之后立刻返回 Producer 不调用 flush 就叫异步(async),默认是 sync。
零拷贝(Zero-copy)技术指在计算机执行操作时,CPU 不须要先将数据从一个内存区域复制到另一个内存区域,从而能够缩小上下文切换以及 CPU 的拷贝工夫。
它的作用是在数据报从网络设备到用户程序空间传递的过程中,缩小数据拷贝次数,缩小零碎调用,实现 CPU 的零参加,彻底消除 CPU 在这方面的负载。
目前零拷贝技术次要有三种类型:
- 间接I/O:数据间接跨过内核,在用户地址空间与I/O设施之间传递,内核只是进行必要的虚拟存储配置等辅助工作;
- 防止内核和用户空间之间的数据拷贝:当应用程序不须要对数据进行拜访时,则能够防止将数据从内核空间拷贝到用户空间
- mmap
- sendfile
- splice && tee
- sockmap
- copy on write:写时拷贝技术,数据不须要提前拷贝,而是当须要批改的时候再进行局部拷贝。
4.2 磁盘文件通过网络发送(Broker 到 Consumer)
传统形式实现:先读取磁盘、再用 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 调用实现,整个过程只有两次上下文切换,因而大大提高了性能。
Kafka 在这里采纳的计划是通过 NIO 的 transferTo/transferFrom
调用操作系统的 sendfile 实现零拷贝。总共产生 2 次内核数据拷贝、2 次上下文切换和一次零碎调用,打消了 CPU 数据拷贝
5. 批处理
在很多状况下,零碎的瓶颈不是 CPU 或磁盘,而是网络IO。
因而,除了操作系统提供的低级批处理之外,Kafka 的客户端和 broker 还会在通过网络发送数据之前,在一个批处理中累积多条记录 (包含读和写)。记录的批处理摊派了网络往返的开销,应用了更大的数据包从而进步了带宽利用率。
6. 数据压缩
Producer 可将数据压缩后发送给 broker,从而缩小网络传输代价,目前反对的压缩算法有:Snappy、Gzip、LZ4。数据压缩个别都是和批处理配套应用来作为优化伎俩的。
小总结 | 下次面试官问我 kafka 为什么快,我就这么说
- partition 并行处理
- 程序写磁盘,充分利用磁盘个性
- 利用了古代操作系统分页存储 Page Cache 来利用内存进步 I/O 效率
- 采纳了零拷贝技术
- Producer 生产的数据长久化到 broker,采纳 mmap 文件映射,实现程序的疾速写入
- Customer 从 broker 读取数据,采纳 sendfile,将磁盘文件读到 OS 内核缓冲区后,转到 NIO buffer进行网络发送,缩小 CPU 耗费
文章继续更新,能够微信搜「 JavaKeeper 」第一工夫浏览,无套路支付 500+ 本电子书和 30+ 视频教学和源码,本文 GitHub github.com/JavaKeeper 曾经收录,Javaer 开发、面试必备技能兵器谱,有你想要的。