本文作者:刘涛,阿里云智能技术专家。

01 Compaction Topic介绍

一般来说,消息队列提供的数据过期机制有如下几种,比如有基于时间的过期机制——数据保存多长时间后即进行清理,也有基于数据总量的过期机制——数据分区数据量达到一定值后进行清理。

而 Compaction Topic 是一种基于 key 的数据过期机制,即对于相同 key 的数据只保留最新值。

该特性的应用场景主要为维护状态信息,或者在需要用到 KV 结构时,可以通过 Compaction Topic 将 key-value 信息直接保存到 MQ,从而解除对外部数据库的依赖。比如维护消费位点,可以将消费组加分区作为 key ,将消费位点做 offset ,以消息形式发送到 MQ ,压缩之后,消费时获取最新 offset 信息即可。另外,像 connect 里的 source 信息比如 Binlog 解析位点或其他 source 处理的位点信息均可存到 Compaction Topic。同时 Compaction Topic 也支持 存储 RSQLDB 与 RStreams 的 checkpoint 信息。

02 需要解决的问题

Compaction 过程中,需要解决如下几个问题:

第一,数据写入过程中,数据如何从生产者发送到 broker 并且最终落盘,数据主备之间的 HA 如何保证?

第二,整个 compaction 的流程包括哪几个步骤?如果数据量太大,如何优化?

第三,数据消费时如何索引消息?如果找不到消息指定的 offset 消息,如何处理?

第四,如果有机器故障,如何恢复老数据?

03 方案设计与实现

第一,数据如何写入。

首先写入到 CommitLog,主要为复用 CommitLog 本身的 HA 能力。然后通过 reput线程将 CommitLog 消息按照 Topic 加 partition 的维度拆分到不同文件里,按分区整理消息,同时生成索引。这样最终消息就按 Topic 加 partition的粒度做了规整。

在 compaction 过程中,为什么不在原先的 commitLog 上做规整,而是再额外按分区做规整?原因如下:

  1. 所有数据都会写到 CommitLog ,因此单个 Topic 的数据不连续。如果要遍历单个 topic 的所有数据,可能需要跳着读,这样就会导致大量冷读,对磁盘 IO 影响比较大。
  2. CommitLog 数据有自动过期机制,会将老数据删除,因此不能将数据直接写到 CommitLog,而 CompactionLog 里的老数据为按 key 过期,不一定会删除。
  3. compact 以分区为维度进行。如果多个分区同时做 compact ,效率较低。因为很多分区的 key 同时在一个结构里,会导致同一个分区能够 compact 的数据比较少, 并且 compact 之后也需要重新写一份么,因此,索性就在 compact 之前将消息通过 reput service 重新归整一遍。

Compact 流程如下:

第一步,确定需要做 compaction 的数据文件列表。一般大于两个文件,需要排除当前正在写的文件。

第二步,将上一步筛选出的文件做遍历,得到 key 到 offset 的映射关系。

第三步,根据映射关系将需要保留的数据重新写到新文件。

第四步,用新文件替换老文件,将老文件删除。

第二步的构建 OffsetMap 主要目的在于可以知道哪文件需要被保留、哪文件需要被删除,以及文件的前后关系,这样就可以确定写入的布局,确定布局之后,就可以按照append 的方式将需要保留的数据写到新文件。

此处记录的并非 key 到 value 的信息,而是 key 到 Offset 的信息。因为 value 的数据 body 可能较长,比较占空间,而 offset 是固定长度,且通过 offset 信息也可以明确消息的先后顺序。另外,key 的长度也不固定,直接在 map 存储原始 key 并不合适。因此我们将 MD5 作为新 key ,如果 MD5 相同 key 认为也相同。

做 compaction 时会遍历所有消息,将相同 key 且 offset 小于 OffsetMap 的值删除。最终通过原始数据与 map 结构得到压缩之后的数据文件。

上图为目录结构展示。写入时上面为数据文件,下面为索引,要 compact 的是标红两个文件。压缩后的文件存储于子目录,需要将老文件先标记为删除,将子目录文件与 CQ 同时移到老的根目录。注意,文件与 CQ 文件名一一对应,可以一起删除。

随着数据量越来越大,构建的 OffsetMap 也会越来越大,导致无法容纳。

因此不能使用全量构建方式,不能将所有要 compact 的文件的 OffsetMap 一次性构建,需要将全量构建改为增量构建,构建逻辑也会有小的变化。

第一轮构建:如上图,先构建上面部分的 OffsetMap ,然后遍历文件,如果 offset 小于 OffsetMap 中对应 key 的 offset 则删除,如果等于则保留。而下面部分的消息的offset 肯定大于 OffsetMap 内的 offset ,因此也需要保留。

第二轮构建:从上一次结束的点开始构建。如果上一轮中的某个 key 在新一轮中不存在,则保留上一轮的值;如果存在,则依然按照小于删除、大于保留的原则进行构建。

将一轮构建变为两轮构建后, OffsetMap 的大小显著降低,构建的数据量也显著降低。

原先的索引为 CommitLog Position、Message Size 和 Tag Hush,而现在我们复用了bcq 结构。由于 Compact 之后数据不连续,无法按照先前的方式直接查找数据所在物理位置。由于 queueOffset 依然为单调增排列,因此可以通过二分查找方式将索引找出。

二分查找需要 queueoffset 信息,索引结构也会发生变化,而 bcq 带有 queueoffse 信息,因此可以复用 bcq 的结构。

Queueoffset 在 compact 前后保持不变。如果 queueoffset 不存在,则获取第一个大于 queueoffset 的消息,然后从头开始将所有全量数据发送给客户端。

机器故障导致消息丢失时,需要做备机的重建。因为 CommitLog 只能恢复最新数据,而 CompactionLog 需要老数据。之前的 HA 方式下,数据文件可能在 compact 过程中被被删除,因此也不能基于复制文件的方式做主备间同步。

因此,我们实现了基于 message 的复制。即模拟消费请求从 master 上拉取消息。拉取位点一般从 0 开始,大于等于 commitLog 最小offset 时结束。拉取结束之后,再做一次 force compaction 将 CommitLog 数据与恢复时的数据做一次 compaction ,以保证保留的数据是被压缩之后的数据。后续流程不变。

04 使用说明

生产者侧使用现有生产者接口,因为要按分区做 compact ,因此需要将相同 key 路由到相同的 MessageQueue,需要自己实现相关算法。

消费者侧使用现有消费者接口,消费到消息后,存入本地类 Map 结构中再进行使用。我们的场景大多为从头开始拉数据,因此需要在开始时将消费位点重置到0。拉取完以后,将消息 key 与 value 传入本地 kv 结构,使用时直接从该结构拿取即可。

RocketMQ Compaction Topic的设计与实现的更多相关文章

  1. Kafka vs RocketMQ——多Topic对性能稳定性的影响-转自阿里中间件

    引言 上期我们对比了RocketMQ和Kafka在多Topic场景下,收发消息的对比测试,RocketMQ表现稳定,而Kafka的TPS在64个Topic时可以保持13万,到了128个Topic就跌至 ...

  2. Kafka vs RocketMQ——多Topic对性能稳定性的影响

    引言 上期我们对比了RocketMQ和Kafka在多Topic场景下,收发消息的对比测试,RocketMQ表现稳定,而Kafka的TPS在64个Topic时可以保持13万,到了128个Topic就跌至 ...

  3. RocketMQ补偿方案架构设计

    RocketMQ作为消息中间件,在系统异步化架构中,应用非常广泛.但是我们在享用RocketMQ的同时,也不能百分百完全信赖它.一旦RocketMQ崩溃了,给我们业务带来的也将是毁灭性打击. 因此,我 ...

  4. RocketMQ消息轨迹-设计篇

    目录 1.消息轨迹数据格式 2.记录消息轨迹 3.如何存储消息轨迹数据 @(本节目录) RocketMQ消息轨迹主要包含两篇文章:设计篇与源码分析篇,本节将详细介绍RocketMQ消息轨迹-设计相关. ...

  5. Kafka vs RocketMQ—— Topic数量对单机性能的影响-转自阿里中间件

    引言 上一期我们对比了三类消息产品(Kafka.RabbitMQ.RocketMQ)单纯发送小消息的性能,受到了程序猿们的广泛关注,其中大家对这种单纯的发送场景感到并不过瘾,因为没有任何一个网站的业务 ...

  6. Topic Model的分类和设计原则

    Topic Model的分类和设计原则 http://blog.csdn.net/xianlingmao/article/details/7065318 topic model的介绍性文章已经很多,在 ...

  7. Kafka vs RocketMQ—— Topic数量对单机性能的影响

    引言 上一期我们对比了三类消息产品(Kafka.RabbitMQ.RocketMQ)单纯发送小消息的性能,受到了程序猿们的广泛关注,其中大家对这种单纯的发送场景感到并不过瘾,因为没有任何一个网站的业务 ...

  8. RocketMQ 创建和删除 topic,以及 broker 和 nameserver 之间的心跳

    命令行主类:org.apache.rocketmq.tools.command.MQAdminStartup 客户端创建 topic 程序参数:updateTopic -n localhost:987 ...

  9. rocketmq(1)

    参考: 开源社区:https://github.com/alibaba/RocketMQ rocketmq入门: http://www.cnblogs.com/LifeOnCode/p/4805953 ...

  10. RocketMQ 事务消息

    RocketMQ 事务消息在实现上充分利用了 RocketMQ 本身机制,在实现零依赖的基础上,同样实现了高性能.可扩展.全异步等一系列特性. 在具体实现上,RocketMQ 通过使用 Half To ...

随机推荐

  1. YOLOv5】LabVIEW+OpenVINO让你的YOLOv5在CPU上飞起来

    前言 上一篇博客给大家介绍了使用opencv加载YOLOv5的onnx模型,但我们发现使用CPU进行推理检测确实有些慢,那难道在CPU上就不能愉快地进行物体识别了吗?当然可以啦,这不LabVIEW和O ...

  2. 齐博x1标签实例:标签的嵌套用法,调用聚合数据

    齐博标签非常强大,可以让不懂程序的你,轻松就能实现所见即所得. 下面跟大家讲解一下,最复杂的运用, 同时使用了union 动态变量参数 与 分页处理标签 比如下面这张图,不仅仅想调用圈子,还想同时调用 ...

  3. LcdToos如何在线对屏进行读写指令调试

    在实际屏调试过程中,工程师经常需要对屏的寄存器频繁进行参数修改和读取测试,LcdTools针对这个做了很好的支持,可以在线进行指令调试,大大提高调试效率. 打开点屏工程,连接PX01并使模组上电点亮. ...

  4. 19.drf response及源码分析

    源代码位于:response.py   REST framework 提供一个 Response 类来支持 HTTP内容协商,该类允许返回可以呈现为多种类型的内容,具体取决于客户端的请求.   这个 ...

  5. 「MySQL高级篇」MySQL索引原理,设计原则

    大家好,我是melo,一名大二后台练习生,大年初三,我又来充当反内卷第一人了!!! 专栏引言 MySQL,一个熟悉又陌生的名词,早在学习Javaweb的时候,我们就用到了MySQL数据库,在那个阶段, ...

  6. mybatis-特殊查询

    特殊SQL的执行 模糊查询 /** * 根据用户名进行模糊查询 * @param username * @return java.util.List<com.atguigu.mybatis.po ...

  7. packet Capture 手机抓包工具

    packet Capture packet Capture 是一款免root的app, 运行在安卓平台上,用于捕获http/https网络流量嗅探的应用程序 特点: 捕获网络数据包,并记录太慢,使用中 ...

  8. kubernetes之kubectl与YAML详解1

    k8s集群的日志,带有组件的信息,多看日志. kubectl命令汇总 kubectl命令汇总 kubectl命令帮助信息 [root@mcwk8s04 ~]# kubectl -h kubectl c ...

  9. 修改服务器ssh端口

    最近,访问公司虚拟机都需要通过堡垒机才能访问了,觉得麻烦.要想不受该规则限制,也有办法,可以通过修改虚拟机ssh端口解决. 下面做个介绍. 1. 通过堡垒机登录虚拟机 2. 修改虚拟机ssh端口 编辑 ...

  10. Nginx的概述和配置

    一.Nginx概述 1.1Nginx的特点 (1)一款高性能.轻量级web服务 稳定性高 系统资源消耗低高 对HTTP并发连接的处理能力 (2)单台物理服务器可支持30000~50000个并发请求 1 ...