Kafka日志段读写分析
引子
之所以写这篇文章是因为之前面试时候被面试官问到(倒)了,面试官说:“你说你对Kafka比较熟?看过源码? 那说说kafka日志段如何读写的吧?”
我心里默默的说了句 “擦...我说看过一点点源码,不是亿点点。早知道不提这句了!”,那怎么办呢,只能回家等通知了啊。
但是为了以后找回场子,咱也不能坐以待毙,日拱一卒从一点点到亿点点。今天我们就来看看源码层面来Kafka日志段的是如何读写的。
Kafka的存储结构
总所周知,Kafka的Topic可以有多个分区,分区其实就是最小的读取和存储结构,即Consumer看似订阅的是Topic,实则是从Topic下的某个分区获得消息,Producer也是发送消息也是如此。
上图是总体逻辑上的关系,映射到实际代码中在磁盘上的关系则是如下图所示:
每个分区对应一个Log对象,在磁盘中就是一个子目录,子目录下面会有多组日志段即多Log Segment,每组日志段包含:消息日志文件(以log结尾)、位移索引文件(以index结尾)、时间戳索引文件(以timeindex结尾)。其实还有其它后缀的文件,例如.txnindex、.deleted等等。篇幅有限,暂不提起。
以下为日志的定义
以下为日志段的定义
indexIntervalBytes
可以理解为插了多少消息之后再建一个索引,由此可以看出Kafka的索引其实是稀疏索引,这样可以避免索引文件占用过多的内存,从而可以在内存中保存更多的索引。对应的就是Broker 端参数 log.index.interval.bytes
值,默认4KB。
实际的通过索引查找消息过程是先通过offset找到索引所在的文件,然后通过二分法找到离目标最近的索引,再顺序遍历消息文件找到目标文件。这波操作时间复杂度为O(log2n)+O(m)
,n是索引文件里索引的个数,m为稀疏程度。
这就是空间和时间的互换,又经过数据结构与算法的平衡,妙啊!
再说下rollJitterMs
,这其实是个扰动值,对应的参数是log.roll.jitter.ms
,这其实就要说到日志段的切分了,log.segment.bytes
,这个参数控制着日志段文件的大小,默认是1G,即当文件存储超过1G之后就新起一个文件写入。这是以大小为维度的,还有一个参数是log.segment.ms
,以时间为维度切分。
那配置了这个参数之后如果有很多很多分区,然后因为这个参数是全局的,因此同一时刻需要做很多文件的切分,这磁盘IO就顶不住了啊,因此需要设置个rollJitterMs
,来岔开它们。
怎么样有没有联想到redis缓存的过期时间?过期时间加个随机数,防止同一时刻大量缓存过期导致缓存击穿数据库。 看看知识都是通的啊!
日志段的写入
1、判断下当前日志段是否为空,空的话记录下时间,来作为之后日志段的切分依据
2、确保位移值合法,最终调用的是AbstractIndex.toRelative(..)
方法,即使判断offset是否小于0,是否大于int最大值。
3、append消息,实际上就是通过FileChannel
将消息写入,当然只是写入内存中及页缓存,是否刷盘看配置。
4、更新日志段最大时间戳和最大时间戳对应的位移值。这个时间戳其实用来作为定期删除日志的依据
5、更新索引项,如果需要的话(bytesSinceLastIndexEntry > indexIntervalBytes)
最后再来个流程图
日志段的读取
1、根据第一条消息的offset,通过OffsetIndex
找到对应的消息所在的物理位置和大小。
2、获取LogOffsetMetadata
,元数据包含消息的offset、消息所在segment的起始offset和物理位置
3、判断minOneMessage
是否为true
,若是则调整为必定返回一条消息大小,其实就是在单条消息大于maxSize
的情况下得以返回,防止消费者饿死
4、再计算最大的fetchSize
,即(最大物理位移-此消息起始物理位移)和adjustedMaxSize
的最小值(这波我不是很懂,因为以上一波操作adjustedMaxSize
已经最小为一条消息的大小了)
5、调用 FileRecords
的 slice
方法从指定位置读取指定大小的消息集合,并且构造FetchDataInfo
返回
再来个流程图:
小结
从哪里跌倒就从哪里爬起来对吧,这波操作下来咱也不怕下次遇到面试官问了。
区区源码不过尔尔,哈哈哈哈(首先得要有气势)
实际上这只是Kafka源码的冰山一角,长路漫漫。虽说Kafka Broker都是由Scala写的,不过语言不是问题,这不看下来也没什么难点,注释也很丰富。遇到不知道的语法小查一下搞定。
所以强烈建议大家入手源码,从源码上理解。今天说的 append
和 read
是很核心的功能,但一看也并不复杂,所以不要被源码这两个字吓到了。
看源码可以让我们升入的理解内部的设计原理,精进我们的代码功力(经常看着看着,我擦还能这么写)。当然还有系统架构能力。
然后对我而言最重要的是可以装逼了(哈哈哈)。
情景剧
老白正目不转睛盯着监控大屏,“为什么?为什么Kafka Broker物理磁盘 I/O 负载突然这么高?”。寥寥无几的秀发矗立在老白的头上,显得如此的无助。
“是不是设置了 log.segment.ms
参数 ? 试试 log.roll.jitter.ms
吧”,老白抬头间我已走出了办公室,留下了一个伟岸的背影和一颗锃亮的光头!
“我变秃了,也变强了”
Kafka日志段读写分析的更多相关文章
- kafka与zookeeper读写分析
kafka的读写都通过leader完成,而zookeeper只有写要通过leader而读可以通过任意follower,我觉得造成这种差异的原因还是在于使用场景. kafka的设计目标是实现一个高吞吐的 ...
- Kafka技术内幕 读书笔记之(六) 存储层——日志的读写
-Kafka是一个分布式的( distributed ).分区的( partitioned ).复制的( replicated )提交日志( commitlog )服务 . “分布式”是所有分布式系统 ...
- ActiveMQ、RabbitMQ、RocketMQ、Kafka四种消息中间件分析介绍
ActiveMQ.RabbitMQ.RocketMQ.Kafka四种消息中间件分析介绍 我们从四种消息中间件的介绍到基本使用,以及高可用,消息重复性,消息丢失,消息顺序性能方面进行分析介绍! 一.消息 ...
- kafka知识体系-kafka设计和原理分析
kafka设计和原理分析 kafka在1.0版本以前,官方主要定义为分布式多分区多副本的消息队列,而1.0后定义为分布式流处理平台,就是说处理传递消息外,kafka还能进行流式计算,类似Strom和S ...
- 关于Kafka日志留存策略的讨论
关于Kafka日志留存(log retention)策略的介绍,网上已有很多文章.不过目前其策略已然发生了一些变化,故本文针对较新版本的Kafka做一次统一的讨论.如果没有显式说明,本文一律以Kafk ...
- 离线部署ELK+kafka日志管理系统【转】
转自 离线部署ELK+kafka日志管理系统 - xiaoxiaozhou - 51CTO技术博客http://xiaoxiaozhou.blog.51cto.com/4681537/1854684 ...
- RabbitMQ,RocketMQ,Kafka 消息模型对比分析
消息模型 消息队列的演进 消息队列模型 发布订阅模型 RabbitMQ的消息模型 交换器的类型 direct topic fanout headers Kafka的消息模型 RocketMQ的消息模型 ...
- Kafka之工作流程分析
Kafka之工作流程分析 kafka核心组成 一.Kafka生产过程分析 1.1 写入方式 producer采用推(push)模式将消息发布到broker,每条消息都被追加(append)到分区(pa ...
- Kafka Producer相关代码分析【转】
来源:https://www.zybuluo.com/jewes/note/63925 @jewes 2015-01-17 20:36 字数 1967 阅读 1093 Kafka Producer相关 ...
随机推荐
- MacOS开发环境搭建
1 Java 安装jdk 下载安装即可,没什么可说的,着重说一下配置mac下的环境变量 $ /usr/libexec/java_home -V #查看安装的jdk版本和路径 $ vim ~/.bash ...
- C# Mongo DB 修改多层嵌套集合中的字段
C# Mongo DB 修改嵌套集合中的字段 虽然c#的mongo 驱动很强大,而且还支持linq,但是一些复杂的操作语句还是比较困难 这里我用Bson实现功能 这是模型(我这里有多层嵌套) publ ...
- 两台Windows Server 2012 R2数据库同步
文件服务器/备库(192.168.0.1) 数据库服务器/备份文件服务器(192.168.0.2) 数据库实时同步 一.在主数据库服务器里,同样打开隐藏文件,找到C:\ProgramData\MySQ ...
- failed to resolve org.junit.platform
IDEA提示:failed to resolve org.junit.platform,如下图 方法一:修改Maven镜像 D:\Program Files\apache-maven-3.6.3-pc ...
- go语言之函数及闭包
一:函数 1 概述: 函数是 Go 程序源代码的基本构造单位,一个函数的定义包括如下几个部分,函数声明关键字 也町. 函数名.参数列表.返回列表和函数体.函数名遵循标识符的命名规则, 首字母的大小写决 ...
- Labview学习之路(十)文本文件再次写入不覆盖
- java 注解开发
目录 注解 JDK自带的注解三个 注解分类 按照运行机制 按照来源分类 自定义注解的语法要求 元注解 解析注解 获取注解的注解 Spring中的注解 组合注解 注解 JDK自带的注解三个 @Overr ...
- 如何使用zabbix监控公网环境的云服务器(从小白到高级技术顾问!!!)
问题:当我们在本地部署了一台Zabbix服务器后,想要对云上的服务器做监控.但是zabbix一个在内网,云服务器一个在公网,网络环境不同该如何解决?能否检测到云服务器数据? 思路:使用NAT技术,将本 ...
- 小程序开发-媒体组件image
image 图片组件,支持 JPG.PNG.SVG.WEBP.GIF 等格式,2.3.0 起支持云文件ID. 所有属性如下: Tips image组件默认宽度320px.高度240px image组件 ...
- String字符串的最大长度是多少?
在学习和开发过程中,我们经常会讨论 short ,int 和 long 这些基本数据类型的取值范围,但是对于 String 类型我们好像很少注意它的"取值范围".那么对于 Stri ...