
TDMQ Pulsar 推出全新大规模延迟消息能力:通过多级时间轮机制重构延迟消息索引加载和消费进度管理,内存占用可控,空洞影响从全量压缩至分钟级,消息 ID 全程不变,客户端零改造,可稳定支撑百万级延迟消息场景。
一、为什么需要延迟消息?
在分布式系统中,很多业务逻辑并不是"立刻执行",而是"到了某个时间再执行"。消息队列的延迟消息能力,正是为这类场景而生。典型使用场景包括:

这些场景的共同特点:消息的产生和消费之间存在一段可预期的时间差。延迟消息将这段"等待"交给消息队列管理,业务方只需在发送时指定到期时间,消费端在消息到期后自动收到投递,逻辑简洁、解耦彻底。
二、社区方案的核心瓶颈
随着业务规模扩大,当延迟消息量级从万级攀升到百万级,Apache Pulsar 社区方案在两个环节的瓶颈开始显现。
2.1 消费进度持久化:消息空洞问题
社区方案通过 MDP + IDM 管理消费进度:
MDP(MarkDeletePosition):消费进度起始位置,此位置及之前的消息视为已全部消费
IDM(IndividualDeletedMessages):非连续的确认消息集合,记录 MDP 之后非连续的已确认消息位置,通常也称"消息空洞"
当延迟消息较多且到期时间分布不均时,大量未到期消息会卡在消费进度中,形成消息空洞:

以上图为例:1:3、1:7、1:9 是未到期的延迟消息,会导致IDM中会需要记录非连续确认的区间(IDM=[(1:4,1:6],(1:7,1:8]]。
当 IDM 中的数据过多时,无法持久化全量 IDM 数据到 BookKeeper 集群,造成消费进度丢失。主题重新加载后,IDM 中未被持久化的部分会被重新投递,造成大面积重复消费。
2.2 延迟索引加载:内存与重建耗时瓶颈
Apache Pulsar 延迟消息索引默认全量加载到内存。消息规模一旦上来,内存占用和索引重建耗时都会成为不容忽视的瓶颈。社区高版本虽引入了 Bucket 化持久存储来缓解,但 Bucket 的管理、合并及元数据存储(依赖 ZooKeeper 的 Cursor Properties)本身又带来了新的复杂性。
两个瓶颈叠加,在大规模延迟消息场景下会引发重复消费、内存占用高、索引加载耗时长的连锁问题。
三、TDMQ Pulsar 解法:三个关键设计

TDMQ Pulsar 新方案的整体思路:外部多级时间轮索引,不依赖内存构建延迟索引,主题中只记录最近到期的延迟消息,降低消费空洞规模,使得在百万级场景下内存占用与重复消费风险同时可控。
3.1 分层时间轮:索引按需加载,不再"全塞内存"
核心思路:把不同时间跨度的延迟索引拆分到不同维度的索引主题,系统只在临近投递时逐步加载更细粒度的索引,避免全量索引常驻内存。

内存中始终只持有当前加载的索引数据,无论延迟消息总量是十万还是百万,内存占用都可控。索引主题的全部消息消费完成后自动删除,无需人工清理。
3.2 到期重投:将空洞影响压缩至分钟级
核心思路:消息到期时,从业务主题读出原始的延迟消息并重新写入。消费端遇到未到期的延迟消息直接跳过,消费到新写入的延迟消息则正常处理。
未到期的延迟消息直接跳过,不再产生大规模的消息空洞
空洞影响范围从全量延迟消息压缩到临近投递的分钟级窗口内
消费进度的 IDM 数据量大幅下降,持久化压力显著降低
3.3 消息 ID 全程不变,客户端零改造
TDMQ Pulsar 新方案对客户端完全透明——延迟消息照常发送到业务主题,生产消费链路的处理逻辑与原来完全一致,客户端无需任何改造。
服务端保证消息 ID 在生产、存储、消费全链路中保持一致,消息查询、消息轨迹等运维能力天然兼容,无需额外适配。
四、方案对比

五、结语
延迟消息是消息队列中一项基础且高频的能力,小规模下易于实现,大规模下稳定运行并不容易。社区方案在消费进度管理和索引加载机制上的固有约束,使其在面对百万级延迟消息时逐渐力不从心。
TDMQ Pulsar 此次升级围绕三个关键点做了系统性重设计:
三者协同,使延迟消息的规模上限不再受制于单机内存和消费空洞。
如果您的业务也在面临延迟消息的规模挑战,欢迎在评论区留下您的使用场景和问题。
前往接入体验:https://cloud.tencent.com/product/tpulsar。
往期推荐
1-2月产品月报 | TDMQ CKafka Serverless 形态正式商业化,TDMQ MQTT 版推出铂金版
TDMQ MQTT 铂金版正式上线!全组件独占,最高支持100万 TPS,1000万连接数!
A2A over MQTT:腾讯云 TDMQ 创新 Agent 协作新模式

扫描下方二维码关注本公众号,
了解更多微服务、消息队列的相关信息!
