目录

前言

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的数据结构与算法相关推荐

  1. 深入理解数据结构和算法

    hi,大家好,我是阿荣,今天分享一些对数据结构和算法精华总结,希望对大家的面试或者工作有一定的帮助: 看完本文可以学到什么 知道哪些数据结构和算法在实际工作中最常用,最重要 理解一些设计上注意事项(经 ...

  2. 看似简单的搜索引擎,原来背后的数据结构和算法这么复杂?

    来源 | 码海 封图 | CSDN 付费下载于视觉中国 前言 我们每天都在用 Google, 百度这些搜索引擎,那大家有没想过搜索引擎是如何实现的呢,看似简单的搜索其实技术细节非常复杂,说搜索引擎是 ...

  3. 常见数据结构和算法实现(排序/查找/数组/链表/栈/队列/树/递归/海量数据处理/图/位图/Java版数据结构)

    常见数据结构和算法实现(排序/查找/数组/链表/栈/队列/树/递归/海量数据处理/图/位图/Java版数据结构) 数据结构和算法作为程序员的基本功,一定得稳扎稳打的学习,我们常见的框架底层就是各类数据 ...

  4. java有趣的技术分享ppt,java面试数据结构与算法高频考

    前言 本文涵盖了阿里巴巴.腾讯.字节跳动.京东.华为等大厂的Java面试真题,不管你是要面试大厂还是普通的互联网公司,这些面试题对你肯定是有帮助的,毕竟大厂一定是行业的发展方向标杆,很多公司的面试官同 ...

  5. 数据结构与算法邹永林PDF_真香系列:耗时大半个月收整全套「Java架构进阶pdf」没白费,终于可以安心备战2021了!...

    2021就这么来了,马上就要开启备战今年的"金三银四"了.辛亏在2020的最后半月花了我大半个月时间收整了全套的「Java架构进阶pdf」,这一波下来,刷完你就会知道,真真香啊,我 ...

  6. 数据结构与算法学习笔记之先进先出的队列

    前言 队列是一种非常实用的数据结构,类似于生活中发排队,可应用于生活,开发中各个方面,比如共享打印机(先请求先打印),消息队列.你想知道他们是怎么工作的么.那就来一起学习一下队列吧 正文 一.队列的定 ...

  7. Python3-Cookbook总结 - 第一章:数据结构和算法

    第一章:数据结构和算法 Python 提供了大量的内置数据结构,包括列表,集合以及字典.大多数情况下使用这些数据结构是很简单的. 但是,我们也会经常碰到到诸如查询,排序和过滤等等这些普遍存在的问题. ...

  8. 推荐一个关于.NET平台数据结构和算法的好项目

    http://www.codeplex.com/NGenerics 这是一个类库,它提供了标准的.NET框架没有实现的通用的数据结构和算法.值得大家研究. 转载于:https://www.cnblog ...

  9. 数据结构和算法:(3)3.1线性表的顺序存储结构

    -----------------------1.线性表基础操作------------------------ 线性表:(List)由零个或多个数据元素组成的有限序列. 首先他是一个序列,元素之间是 ...

  10. weiss数据结构和算法书的使用说明

    <数据结构与算法分析 C语言描述>Mark Allen Weiss著,冯舜玺译,机械工业出版社.Weiss教授的经典教材三部曲之一,其中的C语言描述版本,也就是本书,被称为20世纪最重要的 ...

最新文章

  1. 利用svn自动同步更新到网站服务器 -- 网摘
  2. UIView 弹出动画
  3. HJ106 字符逆序
  4. Android 通过腾讯WebService API获取 地址经纬度
  5. cognito_将Amazon Cognito与单页面应用程序(Vue.js)集成
  6. 单选按钮:after_选择的按钮:将ToggleButtons用作单选按钮
  7. 错误提示之:SQL—无法在服务器上访问指定的路径或文件。请确保您具有必需的安全权限且该路径或文件存在。...
  8. 1.2.2合并线程(Joining Threads)
  9. Java笔记:final修饰符
  10. sping加载bean都发生了些什么
  11. 《斯坦福算法博弈论二十讲》学习笔记(持续更新)
  12. SovitChart工具1分钟快速开发前端统计图表
  13. 微信支付解决多商户平台收款
  14. android吉他谱组件,Paranoid Android drum吉他谱
  15. android 人物走动_Android 中通过切割图片创建人物行走动画
  16. 数学--数论--欧几里得定理和拓展欧几里得定理
  17. [JZOJ 5804] 简单的序列
  18. 致力乡村振兴 从玉农业-林裕豪:中国金控优势强化政企合作
  19. 【器件】红外接收二极管和红外接收三极管
  20. 层次聚类算法之single-linkage和complete-linkage(C语言实现)

热门文章

  1. git里面的文件怎么删不掉_彻底删除git中没用的大文件
  2. win7访问linux共享没有权限设置,局域网共享时提示:你没有权限访问,请与网络管理员联系...
  3. 万用表二极管档位点亮发光二极管LED
  4. 中职计算机应用基础表格制作说课稿,表格制作的说课稿
  5. 错误: 实例 ruiy 执行所请求操作失败,实例处于错误状态。: 请稍后再试 [错误: #39;ascii#39; codec can#39;t decode byte 0xe6 in po...
  6. Talib macd函数探究
  7. 使用python print打印函数返回值多一个None的问题探究
  8. Linux 压测工具 stress 安装下载
  9. Linux系统压力测试工具stress
  10. MPAndroidChart的BarChart用法