Kafka的数据结构与算法
目录
前言
kafka的分治思想
01
第1次分治
02
第2次分治
03
第3次分治
04
第4次分治
05
kafka的查询逻辑
结语
前言
作为大数据时代背景下,流数据处理的明星组件,kafka的重要性不言而喻,网上解读kafka的文章可以说不计其数,有解释各种概念的,也有各种案例说明的。但是能讲到kafka核心干货的文章,却并不多见。
大家都知道它快,那它为什么快呢?快的原因是什么呢?
对于kafka的一些基本概念和基本知识点,这里不再赘述,本文将从kafka的数据结构和查找算法这个视角,来聊一聊kafka如何做到快的。
kafka的分治思想
任何存储组件,其数据最终都会落到磁盘上,比如我们最熟悉的MySQL,其在磁盘上的存储的文件大致分为数据文件和索引文件两大类。
kafka存储数据(消息本身)的文件的数据结构是数组,数组的特点就是:数据间位置连续,如果按照顺序读取,或者追加写入的话,其时间复杂度为O(1),效率最高。
那按照这个思路来想的话,是不是kafka的数据存储用一个数组的数据结构就能满足要求了呢?
试想,如果kafka设计只用一个数组来存储数据,一来如果数据量特别大时,对这个单一数组的无论是写入压力还是读取压力是不是都特别大;二来如果只用一个数组,那么它后台存储的数据只能用到一块磁盘,对这块磁盘的IO来说将是非常大的一个考验。
那么怎么办?
我们知道,针对大数据量的场景,要想快,有一个核心指导思想就是:分治。
kafka正是采用了这种思想,并将该思想发挥到了极致的典型案例。
01
第1次分治
kafka通过topic给用户提供数据的读写,对于不同的业务来说,可以定义不同的topic来达到数据分治的目的,不同的业务写入或者读取不同的topic,且不同的topic会尽可能分散在不同的broker中,提高数据的IO效率。
一个kafka实例可以创建多个topic
虽然kafka没有限制topic的个数,但是也不要盲目多建,因为越多的topic,代表着越多的数据存储单元,容易导致同一个topic的数据在磁盘存储位置的不连续,从而降低数据读写的IO。
02
第2次分治
对于kafka的topic,我们在创建之初可以设置多个partition来存放数据,对于同一个topic的数据,每条数据的key通过哈希取模被路由到不同的partition中(如果没有设置key,则根据消息本身取模),以此达到分治的目的。
同样,对于每个topic的partition数量来说,也不宜过多,因为partition是kafka管理数据的基本逻辑组织单元,越多的partition意味着越多的数据存储文件(一个partition对应至少3个数据文件),同样容易隔断磁盘数据的连续性,影响数据读写的IO性能。
另外,过多的partition还会导致broker的操作系统内存OOM,即每一个partition文件至少对应2个索引文件(至少1个.index文件和1个.indextime文件),而索引文件是需要常驻内存的,因此partition数量不宜过多。
官网给出的partition不宜过多的理由之一。
03
第3次分治
你以为将topic中的数据均匀分布到各个partition中就完了吗?
当然不是。
试想,如果你作为一个consumer,想从一个特定的位置进行数据消费,比如特定的时间点或者特定的数据偏移量(offset),该怎么办呢?
如果按照一般的思维,就得从存储数据的这个文件从头遍历,每个partition的数据都要遍历一次,直到找到目标时间的数据或者目标偏移量的数据为止,很明显,这样的时间复杂度为O(n),如果数据量很大,这个寻址的时间消耗将是巨大的,且会带来非常高的磁盘IO,效率低下。
那为了快速找到目标数据的位置,可以有哪些办法呢?
索引+分治。
即原本一个partition对应一个文件的情况,变成了一个partition对应多个不同类型的文件,kafka将文件根据不同的功能分成了3大类:
kafka的数据文件类型
.index文件:offset索引文件,用来记录log文件中真实消息的相对偏移量和物理位置,为了减少索引文件的大小,这里用了一种比较聪明的做法,叫稀疏索引,即只记录相对offset的范围段(后文详细说明),可用于快速定位目标offset的消息;
其中参数:segment.index.bytes用来控制.index文件的大小,默认最大10MB;
参数:index.interval.bytes用来设置索引文件记录真实消息的消息间隔大小,默认为4KB,即上一条索引记录和下一条索引记录中间会间隔4KB的消息。
.timeindex文件:时间索引文件,类比.index文件,用来记录log文件中真实消息写入的时间情况,跟offset索引文件功能一样,只不过这个以时间作为索引,用来快速定位目标时间点的数据;
.log文件:用来记录producer写入的真实消息,即消息体本身;
这样一来,consumer如果想要去找到特定偏移量或者时间点的数据,不需要直接去消息文件中找了,而是去.index文件或者.timeidex文件中查找,而这两个作为索引文件,是需要加载到内核态内存中的,这样一来,通过这种索引+分治的手段,就可以快速找到目标数据。
但是,如果partition的数据都写到这3个文件中,随着数据的不断写入,这3个文件就会越来越大,会导致它们的读写IO也会越来越高,负荷越来越重。
那么怎么办?
继续分治...
04
第4次分治
单个文件过大,怎么办?
拆!
kafka对单个.index文件、.timeindex文件、.log文件的大小都有限定(通过不同参数配置),且这3个文件互为一组,当.log文件的大小达到阈值则会自动拆分形成一组新的文件。如图所示:
这样一来,将原本过大的文件进行了分治拆解,减小了单个文件的读写压力。
将数据文件拆分为多个,还会带来其他的一些好处。比如对需要的索引文件的加载,由于索引文件是需要预先加载到内存的,如果单个索引文件过大,会导致对内存的消耗过大;
此外,对过期数据的删除也会有帮助,可以以文件为单位进行数据删除,而不是删除文件中的某一个部分数据,效率更高。
这种将数据拆分成多个的小文件叫做segment,一个log文件代表一个segment。
segment的大小由参数log.segment.bytes来控制,默认为1073741824byte(1GB),
以上,就描述完了kafka在数据存储结构上采用的分治思想。
但是,如果想快速定位到某条特定的消息(根据offset或者消息时间),光有分治还不行啊,必须得有路由和索引才可以。
分治的作用只是将大份数据进行打散成多份小数据,而如果想要快速定位到其中的某条数据,还必须要有索引或者路由策略才可以。否则,查找效率一样不佳。
接下来,再看看kafka的查询逻辑...
05
kafka的查询逻辑
分治思想解决了数据集中的问题,将原本一份很大的数据进行了多个层次的拆分,让数据分散到多台服务器(多个broker)的多个不同目录,以及同个目录下的多个文件中。
那么如何解决快速定位到想要开始消费的数据呢?以offset定位为例。
对于消费者来说,想要消费哪个topic的数据,kafka先通过topic名字找到其topic,其时间复杂度为O(1),然后到每个partition中定位具体的offset,如何定位呢?
按顺序查找吗?
当然不是。
前面不是说有索引文件吗?每个消息的segment对应2个索引文件,一个.index文件和一个.timeindex文件,目的就是用来快速找到目标offset的消息。
来看一眼单个partition的文件结构:
单个partition的文件布局
看到的这个文件结构是按照时间进行倒叙排列的,每3个为一组,每一组的文件名都一样,命名方式为当前segment中最小的offset,其中保存最小offset的segment的log文件为:00000000000000000000.log,即offset从0开始的segment。
于是,可以看到图中所有segment的起始offset依次为(其中起始offset为0的的segment因为删除策略被删除了):
单个partition中每个segment的起始offset
那么如果想查找到某一个特定的offset属于哪个segment,用什么办法呢?
很明显:二分查找。
比如我要查找offset为2999999的消息,那么通过二分查找即定位到图中的2890012和3791564之间,而因为2999999<3791564,因此该offset的消息就定位到在2890012这个segment中,即在日志文件00000000000002890012.log中。
但是只定位到00000000000002890012.log这个文件还不够啊,因为这个日志文件本身还是很大的(默认1GB),要想精确定位到想要的消息位置,怎么办呢?
总不能按顺序遍历吧?
这个时候.index文件就派上用场了,上文提到.index文件是用来记录.log文件中消息的相对offset的,那如何进一步精确定位到想要消息的具体位置呢?
先来看看.index文件跟.log文件的映射关系(因为这个两个文件是通过二进制存储的,不能直接查看,下面以示意图表示):
通过.index文件查找.log文件中的消息
可以看到,.index文件中记录的是offset在对应.log文件中实际数据的位置,但是考虑到.index文件的大小,方便读到内存中,所以它不可能记录每一个offset的数据位置,怎么办呢?
于是就用稀疏的记录方式,即有一些记录,有一些不记录,而且是大部分不记录,只记录小部分。
具体的记录方式为:先记录一条offset的位置,然后跨过特定数据量的间隔(默认4KB),再记录下一条,且整个记录过程是顺序的,而这个跨过特定的数据量由参数log.index.interval.bytes来控制,这样一来.index文件就会只占用很少的空间,方便直接读取到内存中,进行offset与实际数据物理位置的映射,方便高效查找。
弄清楚了.index文件与.log文件的对应关系,再回到刚才那个疑问,如何快速查找到offset为2999999的消息呢?
很明显,还是二分查找,通过O(logN)的查找复杂度,在.index文件中定位到其物理位置在相对偏移量为109970(109970+2890012=2999982为小于2999999的最大偏移量)和110028(110028+2890012=3000040 为大于2999999最小的偏移量)这两个索引中间。
即定位到offset为2999999这条消息的具体物理位置在位置244421和位置344498之间。
可还是还没有完全找到啊?
接下来怎么找?
因为kafka中存储的消息是连续的,即按顺序存储的。
所以既然知道了目标消息在位置244421和位置344498之间,那么最后一步的查找,很明显还是还是二分找找,最终定位到位置为344486的消息即为目标消息。
如果想基于时间这个方式对消息进行查找,其查找的方式跟offset查找的思路是一样的,这里不再赘述。
结语
以上,可以看出kafka通过4次分治的理念对数据进行了拆分,然后又通过3次二分查找最终定位到目标消息。
即4次分治,3次二分,就是kakka对应的数据存储结构和查找算法。
(文章转自我的公众号[Anryg是码农],欢迎关注获取更多优质内容)
Kafka的数据结构与算法相关推荐
- 深入理解数据结构和算法
hi,大家好,我是阿荣,今天分享一些对数据结构和算法精华总结,希望对大家的面试或者工作有一定的帮助: 看完本文可以学到什么 知道哪些数据结构和算法在实际工作中最常用,最重要 理解一些设计上注意事项(经 ...
- 看似简单的搜索引擎,原来背后的数据结构和算法这么复杂?
来源 | 码海 封图 | CSDN 付费下载于视觉中国 前言 我们每天都在用 Google, 百度这些搜索引擎,那大家有没想过搜索引擎是如何实现的呢,看似简单的搜索其实技术细节非常复杂,说搜索引擎是 ...
- 常见数据结构和算法实现(排序/查找/数组/链表/栈/队列/树/递归/海量数据处理/图/位图/Java版数据结构)
常见数据结构和算法实现(排序/查找/数组/链表/栈/队列/树/递归/海量数据处理/图/位图/Java版数据结构) 数据结构和算法作为程序员的基本功,一定得稳扎稳打的学习,我们常见的框架底层就是各类数据 ...
- java有趣的技术分享ppt,java面试数据结构与算法高频考
前言 本文涵盖了阿里巴巴.腾讯.字节跳动.京东.华为等大厂的Java面试真题,不管你是要面试大厂还是普通的互联网公司,这些面试题对你肯定是有帮助的,毕竟大厂一定是行业的发展方向标杆,很多公司的面试官同 ...
- 数据结构与算法邹永林PDF_真香系列:耗时大半个月收整全套「Java架构进阶pdf」没白费,终于可以安心备战2021了!...
2021就这么来了,马上就要开启备战今年的"金三银四"了.辛亏在2020的最后半月花了我大半个月时间收整了全套的「Java架构进阶pdf」,这一波下来,刷完你就会知道,真真香啊,我 ...
- 数据结构与算法学习笔记之先进先出的队列
前言 队列是一种非常实用的数据结构,类似于生活中发排队,可应用于生活,开发中各个方面,比如共享打印机(先请求先打印),消息队列.你想知道他们是怎么工作的么.那就来一起学习一下队列吧 正文 一.队列的定 ...
- Python3-Cookbook总结 - 第一章:数据结构和算法
第一章:数据结构和算法 Python 提供了大量的内置数据结构,包括列表,集合以及字典.大多数情况下使用这些数据结构是很简单的. 但是,我们也会经常碰到到诸如查询,排序和过滤等等这些普遍存在的问题. ...
- 推荐一个关于.NET平台数据结构和算法的好项目
http://www.codeplex.com/NGenerics 这是一个类库,它提供了标准的.NET框架没有实现的通用的数据结构和算法.值得大家研究. 转载于:https://www.cnblog ...
- 数据结构和算法:(3)3.1线性表的顺序存储结构
-----------------------1.线性表基础操作------------------------ 线性表:(List)由零个或多个数据元素组成的有限序列. 首先他是一个序列,元素之间是 ...
- weiss数据结构和算法书的使用说明
<数据结构与算法分析 C语言描述>Mark Allen Weiss著,冯舜玺译,机械工业出版社.Weiss教授的经典教材三部曲之一,其中的C语言描述版本,也就是本书,被称为20世纪最重要的 ...
最新文章
- 利用svn自动同步更新到网站服务器 -- 网摘
- UIView 弹出动画
- HJ106 字符逆序
- Android 通过腾讯WebService API获取 地址经纬度
- cognito_将Amazon Cognito与单页面应用程序(Vue.js)集成
- 单选按钮:after_选择的按钮:将ToggleButtons用作单选按钮
- 错误提示之:SQL—无法在服务器上访问指定的路径或文件。请确保您具有必需的安全权限且该路径或文件存在。...
- 1.2.2合并线程(Joining Threads)
- Java笔记:final修饰符
- sping加载bean都发生了些什么
- 《斯坦福算法博弈论二十讲》学习笔记(持续更新)
- SovitChart工具1分钟快速开发前端统计图表
- 微信支付解决多商户平台收款
- android吉他谱组件,Paranoid Android drum吉他谱
- android 人物走动_Android 中通过切割图片创建人物行走动画
- 数学--数论--欧几里得定理和拓展欧几里得定理
- [JZOJ 5804] 简单的序列
- 致力乡村振兴 从玉农业-林裕豪:中国金控优势强化政企合作
- 【器件】红外接收二极管和红外接收三极管
- 层次聚类算法之single-linkage和complete-linkage(C语言实现)
热门文章
- git里面的文件怎么删不掉_彻底删除git中没用的大文件
- win7访问linux共享没有权限设置,局域网共享时提示:你没有权限访问,请与网络管理员联系...
- 万用表二极管档位点亮发光二极管LED
- 中职计算机应用基础表格制作说课稿,表格制作的说课稿
- 错误: 实例 ruiy 执行所请求操作失败,实例处于错误状态。: 请稍后再试 [错误: #39;ascii#39; codec can#39;t decode byte 0xe6 in po...
- Talib macd函数探究
- 使用python print打印函数返回值多一个None的问题探究
- Linux 压测工具 stress 安装下载
- Linux系统压力测试工具stress
- MPAndroidChart的BarChart用法