TDengine 订阅 vs Kafka — 实现与使用深度对比

分类 :6.数据订阅 TMQ | 篇章:05 TMQ vs Kafka

适用版本:TDengine v3.x(v3.3.x / v3.4.x) vs Apache Kafka 2.x/3.x | 最后更新:2026-06-27

TDengine 内置的 TMQ(Time-series Message Queue)在 API 设计上刻意借鉴 Kafka Consumer 模型,但底层实现与 Kafka 有本质差异:TMQ 是「数据库 WAL 之上的 CDC 订阅」,Kafka 是「独立部署的通用分布式日志」。本文基于 TDengine 源码,从架构、存储、消息模型、消费语义到使用方式,做系统性的技术对比,帮助你理解他们之间的相同点及区别,更好的使用他们。

速查对比

维度 TDengine TMQ Apache Kafka
定位 时序数据库内置 CDC / 数据订阅 通用分布式消息队列 / 事件流平台
部署 taosd 内置,无额外组件 独立 Broker 集群(+ KRaft/ZK)
数据来源 数据库写入自动进入 WAL,订阅侧直接读 WAL 应用显式 Producer.send()
Topic 定义 SQL:CREATE TOPIC ... AS SELECT ... 任意名称 + 字节流消息
分区单位 VGroup(数据库分片) Partition(可配置数量)
存储 与数据库共用 WAL,无独立消息文件 独立 Topic 日志段(.log)
保留策略 WAL_RETENTION_PERIOD / WAL_RETENTION_SIZE retention.ms / retention.bytes
过滤 Topic SQL 在 VNode 端下推执行 客户端或 Kafka Streams / ksqlDB
Schema 强类型(TDengine 列类型 + 元数据事件) 字节流(可选 Schema Registry)
协议 TDengine 私有 RPC(默认 6030) Kafka 二进制协议(默认 9092)
消费 API Kafka 风格(group.idpollcommit Kafka 原生 API
跨系统解耦 弱(绑定 TDengine 生态) 强(事实标准)
典型场景 TDengine 内 ETL、CDC、灾备同步 微服务解耦、多源汇聚、流计算总线

1. 架构对比

1.1 Kafka 架构

复制代码
Producer ──send──→ Broker (Topic/Partition)
                        │
                        ├─ Partition 0 ──→ Log Segment
                        ├─ Partition 1 ──→ Log Segment
                        └─ Partition N ──→ Log Segment
                        │
Consumer Group ←──fetch──┘
  ├─ Consumer-1 → P0, P1
  └─ Consumer-2 → P2, P3

协调者:Group Coordinator(Broker 内嵌)
元数据:KRaft Controller / ZooKeeper

Kafka 是存储与计算分离的消息中间件:Broker 只负责持久化日志,Producer/Consumer 通过独立协议接入,与业务数据库无直接关系。

1.2 TMQ 架构(源码视角)

复制代码
写入请求 → VNode → WAL(持久化)→ MemTable → TSDB
                      ↑
                  TMQ 从这里读取(tq 模块)

Client (tmq_consumer_poll)
    │ subscribe / heartbeat / commit
    ▼
MNode(协调者)
  ├─ mndTopic.c    --- Topic 元数据、物理计划
  ├─ mndConsumer.c --- Consumer 注册、心跳
  └─ mndSubscribe.c --- 消费组、Rebalance、Offset 持久化
    │ poll (Fetch)
    ▼
VNode / STQ(source/dnode/vnode/src/tq/)
  ├─ tqProcessPollReq  --- 处理拉取请求
  ├─ tqScanData        --- 扫描 WAL + 执行 Topic SQL
  └─ tqMeta            --- VNode 端 Offset 缓存

TMQ 的三层分工在源码中清晰可见:

组件 源码位置 职责
客户端 source/client/src/clientTmq.c subscribe / poll / commit,多 VGroup 并发拉取
MNode 协调者 mndConsumer.cmndSubscribe.c 消费组管理、Rebalance、Offset 持久化到 SDB
VNode 执行器 tq.ctqRead.ctqScan.c 从 WAL 读日志、下推过滤、返回结构化数据块

核心差异 :TMQ 不是独立消息服务,而是 VNode 上 STQ(Topic Queue)模块对 WAL 的只读订阅视图;Kafka Broker 则是独立进程,消息生命周期与任何数据库无关。

2. Topic 与消息模型

2.1 Kafka Topic

  • Topic 是逻辑名称,消息为 (key, value, headers) 字节流
  • 分区数在创建时指定,可按 key 哈希路由
  • 消息内容由应用自行序列化(JSON、Avro、Protobuf 等)
  • 支持 Compaction Topic(按 key 保留最新值)

2.2 TMQ Topic(三种订阅类型)

源码 include/common/tmsg.h 定义了三种订阅类型:

c 复制代码
enum {
  TOPIC_SUB_TYPE__DB = 1,      // 整库订阅
  TOPIC_SUB_TYPE__TABLE,       // 超级表订阅
  TOPIC_SUB_TYPE__COLUMN,      // 列订阅(最常用)
};
类型 SQL 示例 行为
列订阅 CREATE TOPIC t AS SELECT ts, c FROM meters WHERE c > 100 按 SQL 投影 + WHERE 过滤
超级表订阅 CREATE TOPIC t AS STABLE meters 订阅该超级表全部列及子表变化
数据库订阅 CREATE TOPIC t AS DATABASE db1 订阅库内所有表变化(全库 CDC)

创建 Topic 时,MNode 在 mndTopic.cprocessAst() 中将 SQL AST 编译为物理执行计划physicalPlan)和输出 Schema(schema),下发到各 VNode。这与 Kafka「空 Topic + 任意字节」的模式截然不同------TMQ Topic 本质是带下推过滤的 CDC 查询定义

2.3 消息内容对比

特性 Kafka Message TMQ Message
格式 字节数组 结构化列数据块(SSDataBlock
表名 需自行编码到 key/value msg.with.table.name=true 时附带
批量 单条或批量(batch.size 单次 Poll 可返回多行、多块
元数据事件 无内建 DDL(建表、改表、删表)作为 TMQ_RES_TABLE_META 事件
原始 WAL 不支持 rawData 模式可返回原始写入块(供 taosX 同步)

TMQ 消息类型(include/client/taos.h):

c 复制代码
typedef enum tmq_res_t {
  TMQ_RES_DATA = 1,         // 数据行
  TMQ_RES_TABLE_META = 2,   // 单表元数据
  TMQ_RES_METADATA = 3,     // 批量元数据
  TMQ_RES_RAWDATA = 4       // 原始 WAL 数据块
} tmq_res_t;

3. 分区与并行消费

3.1 Kafka Partition

  • 分区是并行度的基本单位
  • 分区数可独立于集群 Broker 数配置
  • 同一分区内严格有序;跨分区无顺序保证
  • 消费组内:一个分区同一时刻只分配给一个 Consumer

3.2 TMQ VGroup 分区

  • 分区单位是 VGroup(数据库的 vnode 分片),与 TDengine 数据分布绑定

  • Topic 覆盖某数据库时,该库所有 VGroup 都是该 Topic 的「分区」

  • Rebalance 时 MNode(mndSubscribe.c)将 VGroup 平均分配给组内 Consumer

  • 同组 Consumer 数 ≤ VGroup 数,超出部分空转(源码注释与测试均验证此约束)

    Topic 覆盖 6 个 VGroup,消费组 3 个 Consumer:

    复制代码
    Consumer-1: VG1, VG2
    Consumer-2: VG3, VG4
    Consumer-3: VG5, VG6

    不同 group.id 的组互不影响 → 广播语义

与 Kafka 的关键差异

Kafka TMQ
分区数调整 alter topic 增删分区 随数据库 vgroups 或 split 变化,自动触发 Rebalance
分区与数据亲和 按 key 哈希,与存储无关 子表按一致性哈希落入 VGroup,同子表写入在同一 VGroup
顺序保证 分区内有序 VGroup 内按 WAL 写入顺序;跨 VGroup 无全局序

4. 存储与数据保留

4.1 Kafka 存储

复制代码
/data/kafka/
  topic-A-0/
    00000000000000000000.log
    00000000000000000000.index
    00000000000000000000.timeindex
  • 消息存储在 Broker 本地磁盘(或 Tiered Storage)
  • retention.ms / retention.bytes 独立控制
  • 与上游数据库的存储完全解耦
  • 副本通过 ISR(In-Sync Replicas)机制保证

4.2 TMQ 存储(WAL 复用)

TMQ 不维护独立的消息文件 。消费侧通过 walRefFirstVer / walRefLastVer(见 tqUtil.cextractResetOffsetVal)定位 WAL 版本号,再由 tqScanData 顺序扫描。

复制代码
写入 → WAL Entry (ver=N) → MemTable → TSDB 文件
              ↑
         TMQ Fetch 从 ver=N+1 开始读

保留策略由数据库级参数控制:

sql 复制代码
CREATE DATABASE db
  WAL_RETENTION_PERIOD 2592000   -- 秒,如 30 天
  WAL_RETENTION_SIZE   10000;    -- MB

影响

  • WAL 被截断 → 对应 Offset 不可读 → offset out of range 错误
  • 慢 Consumer 会拖住 WAL 清理(与 Kafka 消费滞后导致磁盘增长类似,但共享同一存储池)
  • 不存在「数据库一份、消息队列一份」的双份存储开销
场景 Kafka TMQ
磁盘占用 Topic 独立占用 与 WAL 共享,无额外副本
历史回溯 保留期内任意消费 保留期内从 WAL 读;支持 Snapshot 模式读 TSDB
写入性能影响 无(独立系统) 无额外写入路径(WAL 本来就要写)

5. Offset 与位点管理

5.1 Kafka Offset

  • 每个 (group, topic, partition) 维护一个 long 型 offset
  • 存储在内部 Topic __consumer_offsets
  • auto.offset.resetearliest / latest / none

5.2 TMQ Offset(源码结构)

include/common/tmsg.h 中 Offset 比 Kafka 更复杂:

c 复制代码
enum {
  TMQ_OFFSET__RESET_NONE = -3,
  TMQ_OFFSET__RESET_EARLIEST = -2,
  TMQ_OFFSET__RESET_LATEST = -1,
  TMQ_OFFSET__LOG = 1,              // WAL 版本号
  TMQ_OFFSET__SNAPSHOT_DATA = 2,    // 快照数据位点
  TMQ_OFFSET__SNAPSHOT_META = 3,    // 快照元数据位点
};

typedef struct {
  int8_t type;
  union {
    struct { int64_t uid; int64_t ts; SValue primaryKey; };  // snapshot
    struct { int64_t version; };                              // log
  };
} STqOffsetVal;
模式 含义 对应 Kafka 概念
LOG WAL 版本号(ver 常规 partition offset
SNAPSHOT_DATA TSDB 文件中的数据快照位点 无直接对应(类似 Kafka 无此模式)
SNAPSHOT_META 元数据快照位点 无直接对应

位点持久化路径:

复制代码
Consumer commit
  → MNode SDB(group + topic + vgroup → offset)
  → VNode tqMeta(本地缓存,加速下次 Poll)

客户端 API(include/client/taos.h)与 Kafka 高度对应:

TMQ API Kafka API 说明
tmq_consumer_poll consumer.poll 拉取消息
tmq_commit_sync commitSync 同步提交位点
tmq_commit_async commitAsync 异步提交
tmq_committed committed 查询已提交位点
tmq_position position 当前消费位置
tmq_offset_seek seek 手动定位
tmq_get_topic_assignment assignment 查看分区分配

配置项也刻意对齐 Kafka 命名:

配置项 默认值(源码) 含义
group.id 必填 消费组 ID
auto.offset.reset --- earliest / latest / none
enable.auto.commit --- 自动提交
auto.commit.interval.ms 5000 自动提交间隔
session.timeout.ms 12000 会话超时
heartbeat.interval.ms 3000 心跳间隔
max.poll.interval.ms 300000 最大 Poll 间隔
td.connect.user/pass --- TDengine 认证

6. 消费流程对比

6.1 Kafka 消费流程

复制代码
① 创建 Consumer(配置 group.id、bootstrap.servers)
② subscribe(topics) 或 assign(partitions)
③ poll(timeout) 循环
④ 处理 records
⑤ commitSync / commitAsync
⑥ close

6.2 TMQ 消费流程(源码链路)

复制代码
① tmq_conf_set → tmq_consumer_new
② tmq_subscribe(topics)
   → clientTmq.c 发送 SCMSubscribeReq 到 MNode
   → mndConsumer.c 注册 Consumer,触发 Rebalance
   → mndSubscribe.c 分配 VGroup,下发 SMqRebVgReq 到 VNode
③ tmq_consumer_poll(timeout)
   → 客户端对各 VGroup 并发发送 SMqPollReq
   → VNode tqProcessPollReq → tqExtractDataForMq
   → tqScanData 读 WAL + 执行 physicalPlan 过滤
   → 返回 SSDataBlock 批次
④ 业务处理
⑤ tmq_commit_sync(msg)
   → MNode 持久化 Offset
⑥ tmq_consumer_close

长轮询 :VNode 端若无新数据,会等待至 fetch.max.wait.ms(默认 DEFAULT_MAX_POLL_WAIT_TIME = 1000ms)超时后返回空,避免客户端空转------行为类似 Kafka 的 fetch.min.bytes + fetch.max.wait.ms

Rebalance 检测mndSubscribe.c):

  • MNode 定时器 TDMT_MND_TMQ_TIMER(间隔 mqRebalanceInterval,默认 2 秒)扫描 Consumer
  • 心跳超时(session.timeout.ms)或长时间不 Poll(max.poll.interval.ms)→ 标记离线 → Rebalance
  • VGroup 分裂(vnode split)也会触发 Rebalance

7. 过滤与计算下推

这是 TMQ 相对 Kafka 最显著的优势之一。

Kafka

复制代码
Producer → [全量消息] → Consumer → 应用层过滤

过滤在消费端完成,或使用 Kafka Streams / Flink / ksqlDB 等外部流处理框架。

TMQ

复制代码
写入 → WAL(全量)
         ↓ Fetch 时
       VNode 执行 Topic physicalPlan(WHERE + 列投影)
         ↓
       Consumer 收到已过滤的结构化数据

源码 mndTopic.c 在创建 Topic 时调用 qCreateQueryPlan 生成物理计划,Rebalance 时通过 SMqRebVgReq.qmsg 下发到 VNode。tqScan.c / executor.c 在扫描 WAL 过程中执行过滤。

限制(源码与测试验证):

  • 仅支持 SELECT 投影 + WHERE 谓词
  • 不支持 JOIN、聚合、窗口、子查询
  • 复杂分析应使用流计算(Stream)或消费后在应用层处理
过滤位置 Kafka TMQ
服务端 无(除 Streams) VNode 下推 SQL
网络传输 全量 过滤后列投影
多消费者 各 Consumer 重复过滤 各 VNode 执行一次,多 Consumer 共享同一份 WAL

8. 投递语义与可靠性

语义 Kafka 实现 TMQ 实现
At-Most-Once 先 commit 再处理 enable.auto.commit=true + 短间隔
At-Least-Once 先处理再 commit(默认推荐) 先处理再 tmq_commit_sync(默认推荐)
Exactly-Once 事务 Producer + Streams EOS 无原生 EOS;需业务幂等(如 ts 主键去重)

两者共同点:

  • Rebalance 时未 commit 的消息可能被重新投递
  • 应用必须设计幂等
  • 不支持 Kafka 式的跨 Topic 事务

TMQ 特有考量:

  • WAL 滚动导致早期 Offset 失效 → 需合理配置 WAL_RETENTION_PERIOD
  • 慢 Consumer 影响 WAL 空间 → 需监控 Lag

9. 使用方式对比

9.1 Kafka 典型用法

java 复制代码
// Producer
Properties props = new Properties();
props.put("bootstrap.servers", "kafka:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
producer.send(new ProducerRecord<>("sensors", "d001", "{\"temp\":25.3}"));

// Consumer
props.put("group.id", "etl-group");
props.put("auto.offset.reset", "earliest");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Collections.singletonList("sensors"));
while (true) {
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));
    for (ConsumerRecord<String, String> r : records) {
        process(r.value());
    }
    consumer.commitSync();
}

9.2 TMQ 典型用法

python 复制代码
from taos.tmq import Consumer

consumer = Consumer({
    "group.id": "etl_group",
    "auto.offset.reset": "earliest",
    "td.connect.user": "root",
    "td.connect.pass": "taosdata",
    "enable.auto.commit": "false",
    "msg.with.table.name": "true",
})
consumer.subscribe(["topic_meters"])

try:
    while True:
        msg = consumer.poll(timeout=1.0)
        if msg is None:
            continue
        for block in msg:
            for row in block:
                process(row)
        consumer.commit()
finally:
    consumer.close()
java 复制代码
// Java --- API 刻意对齐 Kafka
Properties props = new Properties();
props.setProperty(TMQConstants.BOOTSTRAP_SERVERS, "127.0.0.1:6030");
props.setProperty(TMQConstants.GROUP_ID, "etl_group");
props.setProperty(TMQConstants.ENABLE_AUTO_COMMIT, "false");

try (TaosConsumer<Meter> consumer = new TaosConsumer<>(props)) {
    consumer.subscribe(Collections.singletonList("topic_meters"));
    while (running) {
        ConsumerRecords<Meter> records = consumer.poll(Duration.ofMillis(1000));
        for (ConsumerRecord<Meter> r : records) {
            process(r.value());
        }
        consumer.commitSync();
    }
}

9.3 Topic 管理

sql 复制代码
-- TMQ:Topic 是 SQL 对象
CREATE TOPIC topic_meters AS
  SELECT ts, current, voltage FROM meters WHERE current > 100;

CREATE TOPIC topic_all AS STABLE meters;
CREATE TOPIC topic_db AS DATABASE test;

SHOW TOPICS;
SHOW CONSUMERS;
SELECT * FROM information_schema.ins_subscriptions;
DROP CONSUMER GROUP etl_group ON topic_meters;
DROP TOPIC topic_meters;
bash 复制代码
# Kafka:Topic 是独立资源
kafka-topics.sh --create --topic sensors --partitions 6 --replication-factor 3
kafka-consumer-groups.sh --describe --group etl-group

10. 生态与集成

集成方式 Kafka TMQ
流处理 Flink、Spark Streaming、ksqlDB 原生支持 TDengine 流计算(内置);Flink 需自定义 Source
数据集成 Kafka Connect、Debezium CDC taosX(Kafka → TD)、TMQ(TD → TD / 外部)
监控 JMX、Burrow、Cruise Control performance_schema.perf_consumers、taosd 日志
跨集群 MirrorMaker、Cluster Linking taosX 多集群同步、TMQ + tmq_write_raw
多语言客户端 全语言官方/社区客户端 C/Python/Java/Go/Rust/Node.js(Connector 内置)

TDengine 数据写入 Kafka 的典型路径:

复制代码
设备 → Kafka → taosX / Kafka Connect → TDengine

TDengine 内部 CDC 的典型路径:

复制代码
设备 → TDengine 写入 → TMQ 订阅 → ETL / 灾备 / 流计算

11. 性能特征

维度 Kafka TMQ
写入额外开销 独立 Producer 网络 + Broker 持久化 零(复用 WAL 写入路径)
消费吞吐 单分区约百万条/秒(视消息大小) 单 VGroup 约数十万行/秒
水平扩展 增加 Broker + Partition 增加 VGroup 数
端到端延迟 ms 级(取决于 batch 配置) 写入到 WAL 可读 < 10ms + 网络
过滤开销 消费端 CPU VNode 端 CPU(下推)

影响 TMQ 消费性能的关键因素(源码与测试):

  • Topic SQL 复杂度
  • 单 Poll 批量大小(min.poll.rows,默认 4096)
  • Consumer 数量与 VGroup 数匹配度
  • Commit 频率(每次 commit 触发 MNode 写 SDB)

12. 选型决策树

复制代码
数据是否已经/只在 TDengine 中?
  │
  ├─ 是 → 需要订阅数据变化?
  │        ├─ 是 → 优先 TMQ(零额外部署、SQL 过滤、强类型)
  │        └─ 否 → 直接 SQL 查询
  │
  └─ 否 → 数据来自多个异构系统?
           ├─ 是 → Kafka 作为统一总线 + taosX/Connect 入库
           └─ 否 → 评估是否值得为单源引入 Kafka

需要跨组织 / 跨技术栈的事件共享?
  ├─ 是 → Kafka(生态标准)
  └─ 否 → TMQ 足够

需要独立的消息保留策略(与数据库 WAL 解耦)?
  ├─ 是 → Kafka
  └─ 否 → TMQ(调 WAL 保留期即可)

需要 Exactly-Once 跨系统事务?
  ├─ 是 → Kafka 事务 + 外部协调
  └─ 否 → 两者均默认 At-Least-Once + 业务幂等

推荐组合

架构 说明
纯 TMQ 数据写入 TDengine,ETL/同步/告警全部走 TMQ,不部署 Kafka
Kafka + TMQ Kafka 汇聚多源数据入库;库内变化用 TMQ 做二级分发
Kafka + taosX 外部数据通过 Kafka 进 TDengine,不用 TMQ
TMQ + taosX TMQ 订阅源集群,tmq_write_raw 写入目标集群(跨集群灾备)

13. 功能差异速查

功能 Kafka TMQ
Producer API ❌(写入即生产)
消息 Key 路由 ❌(按 VGroup 分片)
消息压缩 ✅(Producer 端) ✅(WAL/传输层)
消息去重(幂等 Producer)
事务消息
Log Compaction
服务端过滤 ✅(Topic SQL)
DDL 变更通知 ✅(Meta 事件)
历史快照消费 ✅(Snapshot 模式)
原始数据同步 ✅(tmq_write_raw
独立部署 ❌(依赖 taosd)
消费 API 兼容 Kafka --- ✅(刻意对齐)

14. 常见问题

Q1: TMQ 能替代 Kafka 吗?

不能简单替代。TMQ 适合「数据已在 TDengine 中、需要 CDC 订阅」的场景;Kafka 适合「多系统解耦、通用事件总线」。若业务仅涉及 TDengine 写入后的数据分发,TMQ 可省掉 Kafka 部署与运维。

Q2: 能否让 Kafka Consumer 直接消费 TMQ?

不能。两者协议不兼容。需要通过应用层桥接,或使用 taosX 将 TMQ 数据写入 Kafka(或反向)。

Q3: TMQ 的消费 API 为什么像 Kafka?

降低迁移成本。Java 连接器提供 TaosConsumer / ConsumerRecord,Python 提供 taos.tmq.Consumer,配置项命名与 Kafka 一致(group.idauto.offset.reset 等)。但底层是 TDengine RPC,不是 Kafka 协议。

Q4: 同一个数据能否同时走 Kafka 和 TMQ?

可以。常见模式:设备 → Kafka → taosX → TDengine 入库;入库后通过 TMQ 订阅做库内 ETL 或灾备同步。两条路径职责不同,互补而非互斥。

Q5: WAL 保留期和 Kafka retention 如何对应?

Kafka TMQ
retention.ms WAL_RETENTION_PERIOD(秒)
retention.bytes WAL_RETENTION_SIZE(MB)
消费滞后监控 perf_consumersend_offset - committed_offset

估算公式:WAL_RETENTION_PERIOD ≥ max_consumer_downtime + safety_margin

参考

系统构架篇

数据模型

存储引擎

查询引擎

数据写入

数据订阅

关于 TDengine

TDengine 专为物联网IoT平台、工业大数据平台设计。其中,TDengine TSDB 是一款高性能、分布式的时序数据库(Time Series Database),同时它还带有内建的缓存、流式计算、数据订阅等系统功能;TDengine IDMP 是一款AI原生工业数据管理平台,它通过树状层次结构建立数据目录,对数据进行标准化、情景化,并通过 AI 提供实时分析、可视化、事件管理与报警等功能。