阿里P10 关涛(观滔)邀你探讨新一代数据平台的架构迭代方向,戳此了解。 了解详情
写点什么

Kafka 凭什么速度那么快?

2020 年 5 月 07 日

Kafka凭什么速度那么快?

Kafka 的消息是保存或缓存在磁盘上的,一般认为在磁盘上读写数据是会降低性能的,因为寻址会比较消耗时间,但是实际上,Kafka 的特性之一就是高吞吐率。


即使是普通的服务器,Kafka 也可以轻松支持每秒百万级的写入请求,超过了大部分的消息中间件,这种特性也使得 Kafka 在日志处理等海量数据场景广泛应用。


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


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


一、写入数据

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


1、顺序写入

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


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


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


  • 磁盘顺序读写速度超过内存随机读写

  • JVM 的 GC 效率低,内存占用大。使用磁盘可以避免这一问题

  • 系统冷启动后,磁盘缓存依然可用


下图就展示了 Kafka 是如何写入数据的, 每一个 Partition 其实都是一个文件 ,收到消息后 Kafka 会把数据插入到文件末尾(虚框部分):



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



两个消费者:


  • Consumer1 有两个 offset 分别对应 Partition0、Partition1(假设每一个 Topic 一个 Partition);

  • Consumer2 有一个 offset 对应 Partition2。


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


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


  • 一是基于时间;

  • 二是基于 partition 文件大小。


具体配置可以参看它的配置文档。


2、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 在读取磁盘时做了哪些优化?


2、基于 sendfile 实现 Zero Copy

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


  • 调用 read 函数,文件数据被 copy 到内核缓冲区

  • read 函数返回,文件数据从内核缓冲区 copy 到用户缓冲区

  • write 函数调用,将文件数据从用户缓冲区 copy 到内核与 socket 相关的缓冲区。

  • 数据从 socket 缓冲区 copy 到相关协议引擎。


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


硬盘—>内核 buf—>用户 buf—>socket 相关缓冲区—>协议引擎


而 sendfile 系统调用则提供了一种减少以上多次 copy,提升文件传输性能的方法。


在内核版本 2.1 中,引入了 sendfile 系统调用,以简化网络上和两个本地文件之间的数据传输。sendfile 的引入不仅减少了数据复制,还减少了上下文切换。


sendfile(socket, file, len);
复制代码


运行流程如下:


  • sendfile 系统调用,文件数据被 copy 至内核缓冲区

  • 再从内核缓冲区 copy 至内核中 socket 相关的缓冲区

  • 最后再 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。


2、批量压缩

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


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

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

  • Kafka 支持多种压缩协议,包括 Gzip 和 Snappy 压缩协议


三、总结

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


本文转载自技术锁话公众号。


原文链接:https://mp.weixin.qq.com/s/5Mcs1zmoyB6b03c1NGotZA


2020 年 5 月 07 日 17:37627

评论

发布
暂无评论
发现更多内容

如何在 Spring 生态中玩转 RocketMQ?

阿里巴巴云原生

Java Serverless 微服务 云原生 中间件

10.4|PPT 教程|内容页之表格使用

青城

冰河公开了其总结的一项重要的编程技能!

冰河

Java 正则表达式 程序员

标准引领 浪潮工业互联网助力澳门质量品牌国际认证联盟成立

浪潮云

工业互联网

初识Golang之安装运行篇

Kylin

golang新手 3月日更 21天挑战

盘点一下数据库的误操作有哪些后悔药?

程序员小毕

Java MySQL 编程 程序员 面试

备战一个月,四闯字节跳动,终圆大厂梦!(附面试经历+复习笔记)

Crud的程序员

Java 编程 程序员 架构

我是如何拿到5大银行offer

小舰

面试 银行 笔试 校园招聘

MySQL自定义变量?学不废不收费

Java王路飞

Java MySQL 程序员 架构 面试

GO训练营第12、13周—— runtime

Glowry

SpringBoot配置文件数据加密自定义开发详解

程序员小毕

Java spring 程序员 面试 springboot

白话讲解,拜占庭将军问题

架构精进之路

分布式 算法 3月日更

2021吊打面试官必备!居然真的有人把1000道对标阿里P7面试真题最优解收录成册,全网开源!

Java王路飞

Java 程序员 架构 面试 分布式

朱嘉明:全面认知区块链的科学特征

CECBC区块链专委会

区块链

【得物技术】TDengine在得物的落地应用

得物技术

数据库 tdengine 数据 sentinel 得物技术

微信小程序开发:如何快速实现添加一条分割线的项目需求

三掌柜

微信小程序 3月日更

安全高可用通信背后的 MySQL 优化实践

融云 RongCloud

利用区块链技术,打造绿色发展的中药材生态链

CECBC区块链专委会

中药材

我从外包辞职了,10000小时后,走进字节跳动拿了offer

云流

Java 编程 程序员 架构 面试

Github上堪称最全的面试题库(Java岗)到底有多香

云流

Java 程序员 架构 面试

面面俱到!阿里分布式全栈系统设计实录Github仅上线6天星标已经高达68K!

程序员小毕

Java 架构 面试 分布式 高性能

使用 Arthas 排查SpringBoot诡异耗时的Bug

互联网架构师小马

SpringBoot + Mybatis实现动态数据源切换

互联网架构师小马

深入剖析数据库事务的隔离级别

小舰

数据库 事务隔离级别 数据库事务

融合发展是区块链的未来 数字通证新模式具有划时代意义

CECBC区块链专委会

数字通证

你真的懂Spring解决循环依赖吗?

云流

Java 架构 Spring Boot

高质量、高并发的实时通信架构设计与探索

融云 RongCloud

实时数据流计算引擎Flink和Spark流计算对比

小舰

大数据 flink spark 流计算

打击虚拟货币洗钱:中国破获比特币跨境洗钱案

CECBC区块链专委会

虚拟货币

Spark提交后都干了些什么?

小舰

大数据 spark Spark调优

大专生阿里/腾讯/京东面经分享:Java面试精选题+架构实战笔记(技术狂补)

比伯

Java 编程 架构 面试 计算机

Kafka凭什么速度那么快?-InfoQ