写在前面:我是「云祁」,一枚热爱技术、会写诗的大数据开发猿。昵称来源于王安石诗中一句
[ 云之祁祁,或雨于渊 ]
,甚是喜欢。写博客一方面是对自己学习的一点点总结及记录,另一方面则是希望能够帮助更多对大数据感兴趣的朋友。如果你也对
数据中台、数据建模、数据分析以及Flink/Spark/Hadoop/数仓开发
感兴趣,可以关注我的动态 https://blog.csdn.net/BeiisBei ,让我们一起挖掘大数据的价值~
每天都要进步一点点,生命不是要超越别人,而是要超越自己! (ง •_•)ง
文章目录
一、背景
师兄最近依然在面试中,和我分享了很多面试题和经验,我都一一拿小本本记下来 ( 超认真 !!)🏃
其实在今天的很多大厂面试里,都会要求能够熟练运用Apache Kafka等至少一种消息队列,Apache Kafka也是我们
面试里的常客。在大多数人的印象中,写磁盘都是比较慢的,可是,为什么Apache Kafka在各大MQ性能的评测中,
还能够击败众多对手,取得不错的成绩呢?
通过师兄遇到的关于 Kafka 问题的暴击三连问 ,让我们一起走进师兄受伤的心 💔 …
二、面试题
- Kafka 为什么快?
- Kafka 和其他消息队列的区别?
- Kafka 这么快,它是如何保证不丢失消息?
三、我的回答思路
其实,在大数据开发岗的面试中,都避免不了 面试官问你 Kafka ,毕竟 Kafka 和 Kafka Stream 耍起来确实很香啊!
我们就先从 Kafka 的 基础知识了解起吧,还不太了解的小伙伴可以先看看这几篇博客,先构建一个技术栈的知识框
架,这样在面试时就能做到无懈可击啦! 💥
Kafka |
---|
【Kafka】(一)kafka 简介与设计、实现分析 |
【Kafka】(十)Kafka 如何实现高吞吐量 |
【Kafka】(十一)Kafka 的备份机制 |
【Kafka】(十四)Kafka 架构深入 |
四、关于 Kafka 为什么这么快
Kafka 的消息是保存或缓存在磁盘上的,一般认为在磁盘上读写数据是会降低性能的,因为寻址会比较消耗时间,但
是实际上,Kafka 的特性之一就是高吞吐率。Kafka 之所以能这么快,无非是:顺序写磁盘、大量使用内存页 、零
拷贝技术的使用…
下面我就从数据写入和读取两方面分析,为大家分析下为什么 Kafka 速度这么快。(参考:阿里工程师告诉你 Kafka 为什么这么快!)
数据写入
Kafka 会把收到的消息都写入到硬盘中,它绝对不会丢失数据。
为了优化写入速度 Kafka 采用了两个技术, 顺序写入和 Memory Mapped File 。
4.1 顺序写入
Kafka 会把收到的消息都写入到硬盘中,它绝对不会丢失数据。为了优化写入速度 Kafka 采用了两个技术, 顺序写入和 MMFile(Memory Mapped File)。
磁盘读写的快慢取决于你怎么使用它,也就是顺序读写或者随机读写。在顺序读写的情况下,磁盘的顺序读写速度和内存持平。因为硬盘是机械结构,每次读写都会寻址->写入,其中寻址是一个“机械动作”,它是最耗时的。所以硬盘最讨厌随机 I/O,最喜欢顺序 I/O。为了提高读写硬盘的速度,Kafka 就是使用顺序 I/O。
而且 Linux 对于磁盘的读写优化也比较多,包括 read-ahead 和 write-behind,磁盘缓存等。
如果在内存做这些操作的时候,一个是 Java 对象的内存开销很大,另一个是随着堆内存数据的增多,Java 的 GC 时间会变得很长。
使用磁盘操作有以下几个好处:
- 磁盘顺序读写速度超过内存随机读写。
- JVM 的 GC 效率低,内存占用大。使用磁盘可以避免这一问题。
- 系统冷启动后,磁盘缓存依然可用。
下图就展示了 Kafka 是如何写入数据的, 每一个 Partition 其实都是一个文件 ,收到消息后 Kafka 会把数据插入到文件末尾(虚框部分):
这种方法有一个缺陷——没有办法删除数据 ,所以 Kafka 是不会删除数据的,它会把所有的数据都保留下来,每个
消费者(Consumer)对每个 Topic 都有一个 Offset
用来表示读取到了第几条数据 。
一般情况下 Offset 由客户端 SDK 负责保存 ,会保存到 Zookeeper 里面 。关于存在硬盘中的消息,Kafka 也有它的解决方法,可以基于时间和 Partition 文件的大小,正常 Kafka 是默认七天的保存,也可以通过命令来修改,以 users topic 为例。
修改kafka 7天 默认保存周期
kafka-topics.sh --zookeeper 6 --alter --topic users --config retention.ms=100000
所以,为了避免磁盘被撑满的情况,Kakfa 提供了两种策略来删除数据:
- 基于时间 (默认七天)
- 基于 Partition 文件大小
4.2 Memory Mapped Files
这个和Java NIO中的内存映射基本相同,在大学的计算机原理里我们学过(划重点),mmf (Memory Mapped Files)直接利用操作系统的Page来实现文件到物理内存的映射,完成之后对物理内存的操作会直接同步到硬盘。mmf 通过内存映射的方式大大提高了IO速率,省去了用户空间到内核空间的复制。它的缺点显而易见–不可靠,当发生宕机而数据未同步到硬盘时,数据会丢失,Kafka 提供了produce.type参数来控制是否主动的进行刷新,如果 Kafka 写入到 mmf 后立即flush再返回给生产者则为同步模式,反之为异步模式。
Kafka 提供了一个参数 producer.type 来控制是不是主动 Flush:
- 如果 Kafka 写入到 mmf 之后就立即 Flush,然后再返回 Producer 叫同步 (Sync)。
- 如果 Kafka 写入 mmf 之后立即返回 Producer 不调用 Flush 叫异步 (Async)。
数据读取
Kafka 在读取磁盘时做了哪些优化?
4.3 基于 Sendfile 实现零拷贝(Zero Copy)
作为一个消息系统,不可避免的便是消息的拷贝,常规的操作,一条消息,需要从创建者的socket到应用,再到操作系统内核,然后才能落盘。同样,一条消息发送给消费者也要从磁盘到内核到应用再到接收者的socket,中间经过了多次不是很有必要的拷贝。
传统 Read/Write 方式进行网络文件传输,在传输过程中,文件数据实际上是经过了四次 Copy 操作,其具体流程细节如下:
- 调用 Read 函数,文件数据被 Copy 到内核缓冲区。
- Read 函数返回,文件数据从内核缓冲区 Copy 到用户缓冲区
- Write 函数调用,将文件数据从用户缓冲区 Copy 到内核与 Socket 相关的缓冲区。
- 数据从 Socket 缓冲区 Copy 到相关协议引擎。
硬盘—>内核 buf—>用户 buf—>Socket 相关缓冲区—>协议引擎
而 Sendfile 系统调用则提供了一种减少以上多次 Copy,提升文件传输性能的方法。在内核版本 2.1 中,引入了 Sendfile 系统调用,以简化网络上和两个本地文件之间的数据传输。Sendfile 的引入不仅减少了数据复制,还减少了上下文切换。相较传统 Read/Write 方式,2.1 版本内核引进的 Sendfile 已经减少了内核缓冲区到 User 缓冲区,再由 User 缓冲区到 Socket 相关缓冲区的文件 Copy。而在内核版本 2.4 之后,文件描述符结果被改变,Sendfile 实现了更简单的方式,再次减少了一次 Copy 操作。
Kafka 把所有的消息都存放在一个一个的文件中,当消费者需要数据的时候 Kafka 直接把文件发送给消费者,配合 mmap 作为文件读写方式,直接把它传给 Sendfile。
4.4 批量发送
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!