终于弄明白了 RocketMQ 的存储模型

news2024/11/17 20:43:38

RocketMQ 优异的性能表现,必然绕不开其优秀的存储模型 。

这篇文章,笔者按照自己的理解 , 尝试分析 RocketMQ 的存储模型,希望对大家有所启发。

1 整体概览

首先温习下 RocketMQ 架构。

215cdc5c3486cb1d908e42bef86caf60.png

整体架构中包含四种角色 :

  • Producer :消息发布的角色,Producer 通过 MQ 的负载均衡模块选择相应的 Broker 集群队列进行消息投递,投递的过程支持快速失败并且低延迟。

  • Consumer :消息消费的角色,支持以 push 推,pull 拉两种模式对消息进行消费。

  • NameServer :名字服务是一个非常简单的 Topic 路由注册中心,其角色类似 Dubbo 中的 zookeeper ,支持 Broker 的动态注册与发现。

  • BrokerServer :Broker 主要负责消息的存储、投递和查询以及服务高可用保证 。

本文的重点在于分析 BrokerServer 的消息存储模型。我们先进入 broker 的文件存储目录 。

消息存储和下面三个文件关系非常紧密:

  1. 数据文件 commitlog

    消息主体以及元数据的存储主体 ;

  2. 消费文件 consumequeue

    消息消费队列,引入的目的主要是提高消息消费的性能 ;

  3. 索引文件 indexfile

    索引文件,提供了一种可以通过 key 或时间区间来查询消息。

RocketMQ 采用的是混合型的存储结构,Broker 单个实例下所有的队列共用一个数据文件(commitlog)来存储。

生产者发送消息至 Broker 端,然后 Broker 端使用同步或者异步的方式对消息刷盘持久化,保存至 commitlog 文件中。只要消息被刷盘持久化至磁盘文件 commitlog 中,那么生产者发送的消息就不会丢失。

Broker 端的后台服务线程会不停地分发请求并异步构建 consumequeue(消费文件)和 indexfile(索引文件)。

2 数据文件

RocketMQ 的消息数据都会写入到数据文件中, 我们称之为 commitlog 。

所有的消息都会顺序写入数据文件,当文件写满了,会写入下一个文件

如上图所示,单个文件大小默认 1G , 文件名长度为 20 位,左边补零,剩余为起始偏移量,比如 00000000000000000000 代表了第一个文件,起始偏移量为 0 ,文件大小为1 G = 1073741824。

当第一个文件写满了,第二个文件为 00000000001073741824,起始偏移量为 1073741824,以此类推。

c59253157ee79d32d3f0a4f43349d392.png

从上图中,我们可以看到消息是一条一条写入到文件,每条消息的格式是固定的。

这样设计有三点优势:

  1. 顺序写

    磁盘的存取速度相对内存来讲并不快,一次磁盘 IO 的耗时主要取决于:寻道时间和盘片旋转时间,提高磁盘 IO 性能最有效的方法就是:减少随机 IO,增加顺序 IO 。

    对比随机和顺序读写在内存和磁盘中的表现

    《 The Pathologies of Big Data 》这篇文章指出:内存随机读写的速度远远低于磁盘顺序读写的速度。磁盘顺序写入速度可以达到几百兆/s,而随机写入速度只有几百 KB /s,相差上千倍。

  2. 快速定位

    因为消息是一条一条写入到 commitlog 文件 ,写入完成后,我们可以得到这条消息的物理偏移量。

    每条消息的物理偏移量是唯一的, commitlog 文件名是递增的,可以根据消息的物理偏移量通过二分查找,定位消息位于那个文件中,并获取到消息实体数据。

  3. 通过消息 offsetMsgId 查询消息数据

    消息 offsetMsgId 是由 Broker 服务端在写入消息时生成的 ,该消息编号包含两个部分:

    • Broker 服务端 ip + port  8个字节;

    • commitlog 物理偏移量 8个字节 。

我们可以通过消息 offsetMsgId ,定位到 Broker 的 ip 地址 + 端口 ,传递物理偏移量参数 ,即可定位该消息实体数据。

3 消费文件

在介绍 consumequeue 文件之前, 我们先温习下消息队列的传输模型-发布订阅模型 , 这也是 RocketMQ 当前的传输模型。

b01067e326a233fd31d2f895c4199806.png

发布订阅模型具有如下特点:

  • 消费独立:相比队列模型的匿名消费方式,发布订阅模型中消费方都会具备的身份,一般叫做订阅组(订阅关系),不同订阅组之间相互独立不会相互影响。

  • 一对多通信:基于独立身份的设计,同一个主题内的消息可以被多个订阅组处理,每个订阅组都可以拿到全量消息。因此发布订阅模型可以实现一对多通信。

因此,rocketmq 的文件设计必须满足发布订阅模型的需求。

那么仅仅 commitlog 文件是否可以满足需求吗 ?

假如有一个 consumerGroup 消费者,订阅主题 my-mac-topic ,因为 commitlog 包含所有的消息数据,查询该主题下的消息数据,需要遍历数据文件 commitlog , 这样的效率是极其低下的。

进入 rocketmq 存储目录,显示见下图:

  1. 消费文件按照主题存储,每个主题下有不同的队列,图中 my-mac-topic 有 16 个队列 ;

  2. 每个队列目录下 ,存储 consumequeue 文件,每个 consumequeue 文件也是顺序写入,数据格式见下图。

每个 consumequeue 包含 30 万个条目,每个条目大小是 20 个字节,每个文件的大小是 30 万 * 20 = 60万字节,每个文件大小约5.72M 。和 commitlog 文件类似,consumequeue 文件的名称也是以偏移量来命名的,可以通过消息的逻辑偏移量定位消息位于哪一个文件里。

消费文件按照主题-队列来保存 ,这种方式特别适配发布订阅模型

消费者从 broker 获取订阅消息数据时,不用遍历整个 commitlog 文件,只需要根据逻辑偏移量从 consumequeue 文件查询消息偏移量 ,  最后通过定位到 commitlog 文件, 获取真正的消息数据。

这样就可以简化消费查询逻辑,同时因为同一主题下,消费者可以订阅不同的队列或者 tag ,同时提高了系统的可扩展性。

4 索引文件

每个消息在业务层面的唯一标识码要设置到 keys 字段,方便将来定位消息丢失问题。服务器会为每个消息创建索引(哈希索引),应用可以通过 topic、key 来查询这条消息内容,以及消息被谁消费。

由于是哈希索引,请务必保证key尽可能唯一,这样可以避免潜在的哈希冲突。

//订单Id   
String orderId = "1234567890";   
message.setKeys(orderId);   

从开源的控制台中根据主题和 key 查询消息列表:

c038e6f12c21e483332caddb2a50d403.png

进入索引文件目录 ,如下图所以:

索引文件名 fileName 是以创建时的时间戳命名的,固定的单个 IndexFile 文件大小约为 400 M 。

IndexFile 的文件逻辑结构类似于 JDK 的 HashMap 的数组加链表结构。

1bd486d46be0c1f3367fa2429de0b588.jpeg

HashMap数据结构

索引文件主要由 Header、Slot Table (默认 500 万个条目)、Index Linked List(默认最多包含 2000万个条目)三部分组成 。

假如订单系统发送两条消息 A 和 B , 他们的 key 都是 "1234567890" ,我们依次存储消息 A  ,  消息 B 。

因为这两个消息的 key 的 hash 值相同,它们对应的哈希槽(深黄色)也会相同,哈希槽会保存的最新的消息 B 的索引条目序号 , 序号值是 4 ,也就是第二个深绿色条目。

而消息 B 的索引条目信息的最后 4 个字节会保存上一条消息对应的索引条目序号,索引序号值是 3  , 也就是消息 A 。

5 写到最后

Databases are specializing – the “one size fits all” approach no longer applies ------ MongoDB设计哲学

RocketMQ 存储模型设计得非常精巧,笔者觉得每种设计都有其底层思考,这里总结了三点 :

  1. 完美适配消息队列发布订阅模型 ;

  2. 数据文件,消费文件,索引文件各司其职 ,同时以数据文件为核心,异步构建消费文件 + 索引文件这种模式非常容易扩展到主从复制的架构;

  3. 充分考虑业务的查询场景,支持消息 key ,消息 offsetMsgId 查询消息数据。也支持消费者通过 tag 来订阅主题下的不同消息,提升了消费者的灵活性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/136278.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何快速传输大文件:4 种大文件传输有效的方法

文件大小正在爆炸式增长,随之而来的挑战是如何仍然以快速、安全的方式发送。从这个意义上说,弄清楚如何快速传输大文件似乎是一项几乎不可能完成的任务。随着工作流程不断适应数字化,这对于自由职业者、业余视频编辑、后期制作公司和广播公司…

深挖产品护城河,鹿客科技打开成长天花板

2000-2010年,随着指纹识别技术开始应用于智能门锁,其产品应用领域也开始从酒店推广到普通家庭。2011年以来,随着人脸识别、生物识别技术的兴起,消费者对于产品的需求度不断提升,智能门锁行业也迎来一轮飞跃式发展。来源…

Visual Studio查看虚函数表C++内存模型

在其他选项这里写上/d1 reportAllClassLayout,它可以看到所有相关类的内存布局,如果写上/d1 reportSingleClassLayoutXXX(XXX为类名),则只会打出指定类XXX的内存布局。近期的VS版本都支持这样配置。 运行程序的话就会…

Git:在实际开发中的使用(够你用十年)

Repository 我们先来认识一下版本库——Repository,接下来我们所有提到的 Git 基础命令,都是基于版本库的。 那么什么是版本库呢?版本库又名仓库,英文名 repository,你可以简单理解成一个目录,这个目录里面…

AcWing 数学知识

质数 模板&#xff1a; // 试除法判断质数 bool is_prime(int x) {if (x < 2) return false;//只需枚举一部分 使得 i< x / i, 时间复杂度为√nfor (int i 2; i < x / i; i )if (x % i 0)return false;return true; }// 试除法分解质因数 void divide(int n) {fo…

免费的 AI 动作捕捉工具 #Rokoko Video

创建动画可能很棘手&#xff0c;但使用对了技术和工具&#xff0c;可以轻松实现看起来很棒且功能流畅的完美动画。“ 为每个创作者配备动捕的力量。”近期&#xff0c;Rokoko 通过推出免费的 AI 动作捕捉工具 Rokoko Video来兑现这一承诺。图片来源 &#xff1a;Rokoko为什么说…

CSS弹性盒子(flexbox)实用指南

CSS弹性盒子&#xff08;flexbox&#xff09;实用指南上一节&#xff1a;《CSS 浮动 及其应用 》| 下一节&#xff1a;《CSS网格布局&#xff08;grid&#xff09;实用指南 》jcLee95&#xff1a;https://blog.csdn.net/qq_28550263?spm1001.2101.3001.5343 邮箱 &#xff1…

复习:矩阵计算与自动求导

矩阵计算 矩阵计算就是讲矩阵如何求导数&#xff0c;所有优化模型的求解也是通过求导数实现的。 首先回忆初高中学习的导数知识&#xff0c;这属于标量的导数。 将导数拓展到不可微的情况&#xff0c;这就是亚导数。 将导数拓展到向量&#xff0c;这就是梯度。 向量对向量求…

java优化-代码重,让代码更优美和简洁

简言 在项目工作中&#xff0c;经常会有优化&#xff0c;有sql优化&#xff0c;项目架构优化&#xff0c;业务层优化&#xff0c;代码结构优化等&#xff0c;这些优化都是为了系统&#xff0c;易维护&#xff0c;易懂&#xff0c;易扩展。下面是我个人总结的一些经验分享与大家…

JavaScript 函数-函数概念,使用,函数参数,返回值,arguments的使用,函数的申明方式...

JavaScript 函数 目录JavaScript 函数1. 函数的概念2. 函数的使用2.1 声明函数2.2 调用函数2.3 函数的封装3. 函数的参数3.1 形参和实参3.2 函数参数的传递过程3.3 函数形参和实参个数不匹配问题4. 函数的返回值4.1 return 语句4.2 return 终止函数4.3 return 的返回值4.4 函数…

喜讯 | 代谢组学领航企业百趣生物完成数千万元A+轮融资!

2023年1月3日&#xff0c;国内代谢组学研究领航企业上海百趣生物医学科技有限公司&#xff08;以下简称“百趣生物”&#xff09;宣布完成数千万元A轮战略融资。本轮融资由金域医学集团及其参股基金科金金域领投&#xff0c;老股东启明创投跟投。 本次融资资金将用于推动公司结…

教育行业课程介绍话术

教育行业&#xff0c;吸引生源是很重要的一项工作&#xff0c;但是机构或企业能吸引到生源的前提一定是学员对于机构所授课程有强烈的兴趣。 前言 教育行业&#xff0c;吸引生源是很重要的一项工作&#xff0c;但是机构或企业能吸引到生源的前提一定是学员对于机构所授课程有强…

MyBatisPlus ---- 插件

MyBatisPlus ---- 插件1. 分页插件a>添加配置类b>测试2. xml自定义分页a>UserMapper中定义接口方法b>UserMapper.xml中编写SQLc>配置文件d>测试3. 乐观锁a>场景b>乐观锁与悲观锁c>模拟修改冲突d>乐观锁实现流程e>MyBatis-Plus实现乐观锁1. 分…

蓝牙耳机什么牌子性价比高?性价比最好的蓝牙耳机排行

近年来&#xff0c;蓝牙耳机越来越成为人们日常生活中常见的数码产品之一&#xff0c;逐渐地&#xff0c;看到更多的音频厂商、手机厂商加入蓝牙耳机市场中来。最近&#xff0c;看到很多人问&#xff0c;蓝牙耳机什么牌子性价比高&#xff1f;下面&#xff0c;我来给大家分享一…

浅谈map和unordered_map的应用场景

map和unordered_map的适用场景 底层结构介绍 map底层是红黑树结构unordered_map底层是哈希结构; Hash适用场景(unordered_map) 内存存角度来说hash因为底层维护了哈希表的存在&#xff0c;内存消耗远大于红黑树&#xff0c;但是因为哈希表增删查改时的直接映射&#xff0c…

OpenFeign服务接口调用

✨ OpenFeign服务接口调用OpenFeign & FeignFeign基本介绍OpenFeign基本介绍二者对比OpenFeign的使用新建Module&#xff1a;cloud-consumer-feign-order80pom依赖application.yml全局配置文件主启动类Service1. 业务逻辑接口FeignClient配置调用provider服务2. 新建Paymen…

第三十九讲:神州无线AC基础管理配置

瘦AP零配置上线&#xff0c;对AP的管理和配置都在AC上进行。AC的基础管理包括AC的无线地址指定及无线功能开启、AP的注册、AP用户数管理、自动信道调整等。 一、配置AC无线IP地址 设置静态的无线IP地址查看AC选取的无线IP地址3.开启无线功能 二、AP注册 1&#xff0e;二层模式…

【网络结构设计】11、E-LAN | 通过梯度传输路径来设计网络结构

文章目录一、背景二、方法2.1 网络设计策略2.2 Partial Residual Networks2.3 Cross Stage Partial Networks2.4 Efficient Layer Aggregation Network三、效果论文&#xff1a;Designing Network Design Strategies Through Gradient Path Analysis 代码&#xff1a;暂无 出…

微服务应用视角解读如何选择 K8s 的弹性策略

头&#xff1a;潘俊峰 前言 微服务架构的出现&#xff0c;拆分了庞大的单体应用&#xff0c;让业务之间的开发与协作变得更加灵活。当面临业务流量增加的场景时&#xff0c;往往需要对一些应用组件进行扩容。K8s 在应用层面提供了 HPA&#xff0c;围绕 HPA 开源社区延伸出了 …

SAP-FI模块 处理自动生成会计凭证增强

FICO-模块一. 相关问题概览1. 固定资产业务过渡科目摘要增强功能-F-022. 固定资产业务过渡科目摘要增强功能-MIGO3. 主营业务收入等科目自动反记账功能二. 问题图片描述1. 固定资产业务过渡科目摘要增强功能-F-022. 固定资产业务过渡科目摘要增强功能-MIGO3. 主营业务收入等科目…