当前位置: 首页 > news >正文

【八股消消乐】浅尝Kafka性能优化

在这里插入图片描述

😊你好,我是小航,一个正在变秃、变强的文艺倾年。
🔔本专栏《八股消消乐》旨在记录个人所背的八股文,包括Java/Go开发、Vue开发、系统架构、大模型开发、具身智能、机器学习、深度学习、力扣算法等相关知识点,期待与你一同探索、学习、进步,一起卷起来叭!

目录

  • 题目
  • 答案
    • 分段与索引
    • 零拷贝
    • 批量操作的优势
    • 简历优化
      • 零拷贝
      • page cache
      • 顺序写
      • 分区多影响写入性能
      • 分区过多如何解决?
      • 分区
      • 分段与索引
      • 批量处理
      • 压缩

题目

💬技术栈:RocketMQ、Kafka、RabbitMQ

🔍简历内容:熟悉Kafka消息队列原理及常见优化手段,如分段与索引、零拷贝、Page Cache、顺序写、分区、批量处理、压缩等。

🚩面试问:Kafka 用到的这些优化技术,很多中间件也用到了,你能举几个例子吗?


在这里插入图片描述

💡建议暂停思考10s,你有答案了嘛?如果你有不同题解,欢迎评论区留言、打卡。


答案

Kafka 本身使用了很多手段来保证高性能,包括零拷贝、page cache(页缓存)、顺序读写、分区分段与索引、批量处理、压缩

分段与索引

总结:topic 加分区定目录,偏移量定文件,索引定位置。

在 Kafka 内部,一个分区的日志是由很多个段(segment)组成的,每个段你可以理解成一个文件。同一个 topic 的文件就存放在以 topic 命名的目录下

在同一个分区内部,Kafka 进一步利用了分段日志和索引来加速消息查找。

在这里插入图片描述
快速定位段文件:段日志文件使用了偏移量来命名。假如说一个文件的名字是 N.log,那么就表示这个段文件里第一条消息的偏移量是 N + 1

所以,Kafka 完全可以根据文件名来进行二分查找,从而快速定位到段文件。

为了加快段文件内的查找,每一个段文件都有两个索引文件

  • 一个是偏移量索引文件,存储着部分消息偏移量到存储位置的映射,类似于 <offset, position> 这种二元组。这个 offset 不是全局 offset,是相对于这个文件第一条消息的偏移量。也就是说假如第一条消息的全局偏移量是 1000,那么偏移量为 1002 的消息的索引项是 <2, pos1>
  • 一个是时间索引文件,存储着时间戳到存储位置的映射,类似于 <timestamp, position> 二元组。

所以整个日志文件目录看上去是这样的。

在这里插入图片描述

以这张图片为例,假如说要查找 topic 为 test_topic,分区为 1,偏移量为 20000 的消息,那么整个过程是这样的。

  1. 在日志目录下找到名字为 test_topic_1 的子目录,里面就放着这个分区的消息日志文件。
  2. 在 test_topic_1 子目录下,根据文件名进行二分查找,可以确定 20000 这条消息应该放在010031.log 这个文件里面。
  3. 利用 010031.index 的内容进行二分查找,查找索引项。如果 20000 恰好有一个索引项 <20000, pos0>,那么就读取 pos0 这个位置的数据。
  4. 如果 20000 没有对应的索引项,就找到比 20000 小的最接近 20000 的索引项,假如有 <19990,pos1>,那么就从 pos1 往后遍历,找到 20000 对应的数据。

对应的根据时间查找也差不多。这里要注意的是,索引文件放的只是部分消息对应的位置,因为 Kafka 希望索引文件能够装入内存。【MySQL 索引的时候是默认索引都在内存里面。】

零拷贝

零拷贝(zero copy):是中间件广泛使用的一个技术,它能极大地提高中间件的性能。所谓的零拷贝,就是指没有 CPU 参与的拷贝

普通IO操作:

在这里插入图片描述

  • DMA (Direct Memory Access):一个独立于 CPU 的硬件。
  • NIC(Network Interface Card):网卡。

一共有四个步骤:

  1. 应用进入内核态,从磁盘里读取数据到内核缓存,也就是读缓存。这一步应用就是发了一个指令,然后是 DMA 来完成的。
  2. 应用把读缓存里的数据拷贝到应用缓存里,这个时候切换回用户态。
  3. 应用进入内核态,把应用缓存里的数据拷贝到内核缓存里,也就是写缓存
  4. 应用把数据从写缓存拷贝到 NIC 缓存里,这一步应用也就是发了一个指令,DMA 负责执行。

这里面总共有四次内核态与用户态的切换

在这里插入图片描述

零拷贝:

在这里插入图片描述

批量操作的优势

批量操作的优势:一个是更少的系统调用和内核态与用户态的切换,还有一个是高效利用网络带宽

在网络传输的时候,每一次发送都有一个固定开销,比如说协议头的部分,这个开销大小和具体的协议设计有关。假如说每个请求大小是 1KB,在网络传输的时候,分 100 次传输 1KB 和 1 次传输 100KB,后者也是明显快很多的。前者需要传输 100 次协议头,而后者只需要传输 1 次协议头

简历优化

案例准备:

  • 公司有没有因为分区或者 topic 太多导致 Kafka 性能衰退的案例?如果有,当时是怎么解决的?
  • 公司内部的 Kafka 用的是机械硬盘还是固态硬盘
  • 公司内部的 Kafka 能撑住的并发量是多大?你的业务并发量是多大
  • 公司内部还有没有别的中间件也使用了类似的优化技术?
  • 你在业务中有没有使用批量处理的技术来优化系统性能?如果有,具体是怎么做的?

回答模板:

零拷贝

零拷贝是中间件设计的通用技术,是指完全没有 CPU 参与的读写操作。

我以从磁盘读数据,然后写到网卡上为例介绍一下。首先,应用程序发起系统调用,这个系统调用会读取磁盘的数据,读到内核缓存里面。同时,磁盘到内核缓存是 DMA 拷贝。然后再从内核缓存拷贝到 NIC 缓存中,这个过程也是 DMA 拷贝。这样就完成了整个读写操作。和普通的读取磁盘再发送到网卡比起来,零拷贝少了两次 CPU 拷贝,和两次内核态与用户态的切换。

page cache

这里说的内核缓存,在 linux 系统上其实就是 page cache

  • Kafka 把数据写入到 page cache 而不是直接刷新到磁盘上,有效减少了真实的 IO 操作次数
  • Kafka 是基于 JVM的,所以直接操作 page cache能够避开 JVM 的垃圾回收。同时也能充分利用操作系统对 page cache 的优化。大多数跟 IO 操作打交道的中间件都有类似的机制,比如说 MySQL、Redis。

不过使用 page cache 的缺陷就是如果消息还没刷新到磁盘上,服务器就宕机了,那么整个消息就丢失了。

顺序写

在计算机里面,普遍认为写很慢,但是实际上是随机写很慢,但是顺序写并不慢。即便是机械硬盘的顺序写也并不一定会比固态硬盘的顺序写慢。

Kafka 在写入数据的时候就充分利用了顺序写的特性。它针对每一个分区,有一个日志文件 WAL(write-ahead log),这个日志文件是只追加的,也就是顺序写的,因此发消息的性能会很好。MySQL、Redis 和其他消息中间件也采用了类似的技术。

在这里插入图片描述

所以早期的时候业界就有人做过实验,一台 Kafka 服务器,把磁盘从机械硬盘切换到固态硬盘,性能虽然有提升,但是并不明显。在固态硬盘很贵的情况下,并不划算。

分区多影响写入性能

但是 Kafka 的顺序写要求的是分区内部顺序写,不同的分区之间就不是顺序写的。所以如果一个 topic 下的分区数量不合理,偏多的话,写入性能是比较差的。

举个例子,假如说要写入 100M 的数据,如果只有一个分区,那就是直接顺序写入 100M。但是如果有 100 个分区,每个分区写入 1M,它的性能是要差很多的。因为一个 topic 至少有一个分区,topic 多也会影响 Kafka 的性能。最好是在创建 topic 的时候就规划好分区,但是如果没规划好,还是得考虑解决

在这里插入图片描述

分区过多如何解决?

如果某个 topic 分区太多了用不上,就可以考虑不用其中的一些分区。假设说我们现在有 32 个分区,但是事实上业务本身用不上那么多分区,那么就可以考虑要求发送者只将消息发送到特定的 16 个分区上。当然,能够直接创建新 topic 是最好的。

在这里插入图片描述

topic 过多的话,可以考虑合并一些 topic,但这也是看业务的。比如说最开始的设计是某个主业务下的子业务都有一个 topic,那么可以考虑这些子业务合并使用一个 topic,然后在里面用 type 等字段来标记是归属于哪个子业务的

多少分区才算多,以及多少分区才会引起性能下降,这和 topic 本身有关,也和业务有关。

不过之前阿里云中间件团队测试过,在一个 topic 八个分区的情况下,超过 64 个 topic 之后,Kafka 性能就开始下降了

分区

Kafka 的分区机制也能提高性能。假如说现在 Kafka 没有分区机制,只有 topic,那么可以预计的是不管是读还是写,并发竞争都是 topic 维度的。而在引入了分区机制之后,并发竞争的维度就变成分区了。如果是操作不同的分区,那么完全不需要搞并发控制

在这里插入图片描述

分段与索引

在 Kafka 中,每一个分区都对应多个段文件,放在同一个目录下。Kafka 根据 topic 和分区就可以确定消息存储在哪个目录内。每个段文件的文件名就是偏移量,假设为 N,那么这个文件第一条消息的偏移量就是 N+1。所以 Kafka 根据偏移量和文件名进行二分查找,就能确定消息在哪个文件里

然后每一个段文件都有一个对应的偏移量索引文件和时间索引文件。Kafka 根据这个索引文件进行二分查找,就很容易在文件里面找到对应的消息。如果目标消息刚好有这个索引项,那么直接读取对应位置的数据。如果没有,就找到比目标消息偏移量小的,最接近目标消息的位置,顺序找过去。整个过程非常像跳表。

批量处理

Kafka 还采用了批量处理来提高性能。Kafka 的客户端在发送的时候,并不是说每来一条消息就发送到 broker 上,而是说聚合够一批再发送。而在 broker 这一端,Kafka 也是同样按照批次来处理的,显然即便同样是顺序写,一次性写入数据都要比分多次快很多。除了 Kafka,很多高并发、大数据的中间件也采用类似的技术,比如说日志采集与上报就采用批量处理来提升性能。

在这里插入图片描述

批量处理高性能原因:

一方面:减少系统调用和内核态与用户态切换的次数。比方说100 个请求发送出去,即便采用零拷贝技术,也要 100 次系统调用 200 次内核态与用户态切换。而如果是一次性发送的话,那么就只需要 1 次系统调用和 2 次内核态与用户态切换。

另外一方面:批量处理也有利于网络传输。在网络传输中,一个难以避免的问题就是网络协议自身的开销。比如说协议头开销。那么如果发送 100 次请求,就需要传输 100 次协议头。如果 100 个请求合并为一批,那就只需要一个协议头。

兜底技术:

正常来说批次总是越大越好,但是批次太大会导致一个后果,就是客户端难以凑够一个批次。比如说 100 条消息一批和 1000 条消息一批,后者肯定很难凑够一个批次。一般来说批量处理都是要兜底的,就是在固定时间内如果都没有凑够某个批次,那么就直接发送。比如说 Kafka 里面生产者就可以通过 linger.ms 参数来控制生产者最终等多长时间。时间到了,即便只有一条消息,生产者也会把消息发送到 broker 上。

压缩

Kafka 为了进一步降低网络传输和存储的压力,还对消息进行了压缩。这种压缩是端到端的压缩,也就是生产者压缩,broker 直接存储压缩后的数据,只有消费者才会解压缩。它带来的好处就是,网络传输的时候传输的数据会更少,存储的时候需要的磁盘空间也更少。当然,缺点就是压缩还是会消耗 CPU。如果生产者和消费者都是 CPU 密集型的应用,那么这种压缩机制反而加重了它们的负担。

在这里插入图片描述


往期精彩专栏内容,欢迎订阅:

🔗【八股消消乐】20250630:消息队列优化—重复消费
🔗【八股消消乐】20250629:消息队列优化—消息丢失
🔗【八股消消乐】20250627:消息队列优化—消息积压
🔗【八股消消乐】20250625:消息队列优化—消息有序
🔗【八股消消乐】20250624:消息队列优化—延迟消息
🔗【八股消消乐】20250623:消息队列优化—系统架构设计
🔗【八股消消乐】20250622:Elasticsearch查询优化
🔗【八股消消乐】20250620:Elasticsearch优化—检索Labubu
🔗【八股消消乐】20250619:构建微服务架构体系—保证服务高可用
🔗【八股消消乐】20250615:构建微服务架构体系—链路超时控制
🔗【八股消消乐】20250614:构建微服务架构体系—实现制作库与线上库分离
🔗【八股消消乐】20250612:构建微服务架构体系—限流算法优化
🔗【八股消消乐】20250611:构建微服务架构体系—降级策略全总结
🔗【八股消消乐】20250610:构建微服务架构体系—熔断恢复抖动优化
🔗【八股消消乐】20250609:构建微服务架构体系—负载均衡算法如何优化
🔗【八股消消乐】20250608:构建微服务架构体系—服务注册与发现
🔗【八股消消乐】20250607:MySQL存储引擎InnoDB知识点汇总
🔗【八股消消乐】20250606:MySQL参数优化大汇总
🔗【八股消消乐】20250605:端午节产生的消费数据,如何分表分库?
🔗【八股消消乐】20250604:如何解决SQL线上死锁事故
🔗【八股消消乐】20250603:索引失效与优化方法总结
🔗【八股消消乐】20250512:慢SQL优化手段总结
🔗【八股消消乐】20250511:项目中如何排查内存持续上升问题
🔗【八股消消乐】20250510:项目中如何优化JVM内存分配?
🔗【八股消消乐】20250509:你在项目中如何优化垃圾回收机制?
🔗【八股消消乐】20250508:Java编译优化技术在项目中的应用
🔗【八股消消乐】20250507:你了解JVM内存模型吗?
🔗【八股消消乐】20250506:你是如何设置线程池大小?
🔗【八股消消乐】20250430:十分钟带背Duubo中大厂经典面试题
🔗【八股消消乐】20250429:你是如何在项目场景中选取最优并发容器?
🔗【八股消消乐】20250428:你是项目中如何优化多线程上下文切换?
🔗【八股消消乐】20250427:发送请求有遇到服务不可用吗?如何解决?

📌 [ 笔者 ]   文艺倾年
📃 [ 更新 ]   2025.7.11
❌ [ 勘误 ]   /* 暂无 */
📜 [ 声明 ]   由于作者水平有限,本文有错误和不准确之处在所难免,本人也很想知道这些错误,恳望读者批评指正!

在这里插入图片描述

http://www.lryc.cn/news/585490.html

相关文章:

  • IAR携手矽力杰与普华基础软件,共推RISC-V车规芯片高安全应用落地
  • 必备软件推荐:1、Everything:Windows 文件查找的终极利器
  • PyInstaller打包完整指南1
  • 【web应用】若依框架前端报表制作与导出全攻略(ECharts + html2canvas + jsPDF)
  • 8-day06预训练模型
  • CReFT-CAD 笔记 带标注工程图dxf,png数据集
  • 上位机知识篇---常见的文件系统
  • 灰盒级SOA测试工具Parasoft SOAtest重新定义端到端测试
  • QT控件 使用QtServer系统服务实现搭建Aria2下载后台服务,并使用Http请求访问Json-RPC接口调用下载退出
  • 《月亮与六便士》:天才的背叛与凡人救赎的残酷辩证法
  • 【时时三省】(C语言基础)通过指针引用数组元素
  • 计算机网络第三章(6)——数据链路层《网桥交换机》
  • 【中文核心期刊推荐】中国农业科技导报
  • 2025最新版Docker讲解/面试/命令/容器化技术
  • 什么是Podman?能否替代Docker?Podman快速入门
  • 雨污管网智慧监测系统网络建设方案:基于SD-WAN混合架构的最佳实践
  • 第三方渗透测试:范围咋定?需供应商同意吗?
  • 正义的算法迷宫—人工智能重构司法体系的技术悖论与文明试炼
  • ICLR 2025 | InterpGN:时间序列分类的透明革命,Shapelet+DNN双引擎驱动!
  • 目标检测:视觉系统中的CNN-Transformer融合网络
  • Day58
  • 5G标准学习笔记14 - CSI--RS概述
  • Set 二分 -> 剑指算法竞赛
  • 基于机器视觉的半导体检测解决方案
  • MySQL内置函数(8)
  • MySQL中使用group_concat遇到的问题及解决
  • 【AI大模型】BERT微调文本分类任务实战
  • spring boot 详解以及原理
  • 力扣-141.环形链表
  • 力扣_二叉搜索树_python版本