在 Kafka 数据上使用 SQL 不需要流式引擎

发布: (2026年1月14日 GMT+8 15:55)
15 min read
原文: Dev.to

Source: Dev.to

在 Kafka 上使用 SQL:数据并不一定需要流处理引擎

在过去的几年里,Kafka 已经从一个单纯的日志系统演变为完整的事件流平台。随着 ksqlDBKafka Streams、以及 Kafka Connect 的成熟,越来越多的团队开始用 SQL 来直接查询和处理 Kafka 中的数据,而不必再额外部署 FlinkSparkBeam 等流处理引擎。

本文将阐述:

  • 为什么在很多场景下,直接在 Kafka 上使用 SQL 已经足够;
  • ksqlDB 提供的 推查询(push query)拉查询(pull query) 的区别;
  • 实际案例展示如何用纯 SQL 完成常见的实时分析需求;
  • 何时仍然需要外部流处理引擎的补充。

1️⃣ 为什么可以直接在 Kafka 上使用 SQL?

  1. 持久化的、可重放的日志
    Kafka 的主题(topic)本质上是一个有序、持久化的日志。只要保留足够的 retention 时间,所有历史事件都可以随时被重新读取,这为 SQL 查询提供了可靠的数据来源。

  2. 内置的状态管理
    ksqlDB 在后台使用 RocksDB 来维护物化视图(materialized view),这相当于在 Kafka 之上自动搭建了一个 键值存储,无需自行实现状态管理。

  3. 统一的序列化/反序列化
    通过 Schema Registry,ksqlDB 能够自动解析 Avro、Protobuf、JSON Schema,让 SQL 语句直接操作结构化字段,而不必手动处理字节流。

  4. 低延迟的查询模型

    • 推查询(Push Query):类似于 SELECT * FROM orders EMIT CHANGES;,实时推送新产生的记录。
    • 拉查询(Pull Query):对已物化的视图执行点查询,例如 SELECT * FROM orders_by_customer WHERE customer_id = 'C123';,返回最新的快照。

这些特性让 SQL 成为在 Kafka 上进行 实时监控、仪表盘、告警 的理想工具。


2️⃣ ksqlDB 的核心概念

概念说明示例
流(STREAM)代表一个永远增长的、不可变的记录序列。CREATE STREAM page_views (user_id STRING, url STRING) WITH (kafka_topic='page_views', value_format='AVRO');
表(TABLE)代表一个随时间更新的键值映射,内部使用 压缩主题(compact topic)。CREATE TABLE user_profiles (user_id STRING PRIMARY KEY, age INT) WITH (kafka_topic='user_profiles', value_format='AVRO');
物化视图ksqlDB 自动为 TABLE聚合查询 创建本地状态存储,以支持快速拉查询。CREATE TABLE orders_by_customer AS SELECT customer_id, COUNT(*) AS order_cnt FROM orders GROUP BY customer_id;
推查询持续输出匹配的记录流。SELECT * FROM orders EMIT CHANGES;
拉查询对物化视图执行一次性查询,返回最新快照。SELECT * FROM orders_by_customer WHERE customer_id='C123';

3️⃣ 实际案例:实时订单监控

假设我们有一个名为 orders 的主题,记录了每笔订单的 order_id、customer_id、amount、order_time。我们希望实现以下需求:

  1. 实时监控每分钟的订单总额(推查询);
  2. 随时查询某个客户的累计订单数(拉查询);
  3. 对异常订单(金额 > 10,000)触发告警(推查询 + 过滤)。

下面是对应的 ksqlDB 语句(代码块保持原样,不翻译):

-- 1. 创建原始流
CREATE STREAM orders (
  order_id STRING,
  customer_id STRING,
  amount DOUBLE,
  order_time BIGINT
) WITH (
  kafka_topic='orders',
  value_format='AVRO',
  timestamp='order_time'
);

-- 2. 每分钟聚合订单总额(推查询)
CREATE TABLE minute_sales AS
  SELECT
    WINDOWSTART AS window_start,
    WINDOWEND   AS window_end,
    SUM(amount) AS total_amount
  FROM orders
  WINDOW TUMBLING (SIZE 1 MINUTE)
  GROUP BY WINDOWSTART, WINDOWEND;

-- 3. 查询某客户累计订单数(拉查询)
CREATE TABLE orders_by_customer AS
  SELECT
    customer_id,
    COUNT(*) AS order_cnt,
    SUM(amount) AS total_spent
  FROM orders
  GROUP BY customer_id;

-- 4. 过滤异常订单并推送告警
CREATE STREAM high_value_orders AS
  SELECT *
  FROM orders
  WHERE amount > 10000;

推查询示例(实时监控)

SELECT window_start, window_end, total_amount FROM minute_sales EMIT CHANGES;

拉查询示例(随时查询)

SELECT order_cnt, total_spent FROM orders_by_customer WHERE customer_id='C123';

通过上述几行 SQL,我们已经完成了 实时聚合、点查询、异常检测,全部在 Kafka 上完成,无需额外的流处理框架。


4️⃣ 何时仍然需要外部流处理引擎?

虽然 ksqlDB 能满足大多数 实时分析轻量级业务逻辑,但在以下场景下,仍然建议使用 Flink、Spark Structured StreamingBeam

场景原因
复杂的窗口语义(如会话窗口、跨键聚合)ksqlDB 的窗口功能相对有限,外部引擎提供更丰富的窗口算子。
自定义函数或机器学习模型需要在流处理中调用 Java/Scala/Python 自定义代码,ksqlDB 只能使用内置或 UDF。
大规模的状态后端当状态大小超过单机 RocksDB 能容纳的范围,需要分布式状态后端(如 Flink 的 RocksDB StateBackend + HA)。
多流/多表联结(跨主题、跨键的复杂 Join)ksqlDB 支持的 Join 类型受限,外部引擎提供更灵活的多流 Join。
容错与一致性要求极高某些业务需要 exactly‑once 语义和事务级别的控制,Flink 等框架在这方面更成熟。

在这些情况下,Kafka 仍然是底层的持久化层,而流处理引擎负责更高级的计算逻辑。两者可以组合使用:先用 ksqlDB 完成快速的实时监控和轻量查询,再在需要时将数据流向 Flink/Spark 进行深度处理。


5️⃣ 小结

  • SQL 已经足够:对于大多数实时监控、仪表盘、告警等业务场景,直接在 Kafka 上使用 ksqlDB 的 推查询拉查询 就能满足需求,省去了部署、运维额外流处理集群的成本。
  • 状态自动管理:ksqlDB 通过内部的 RocksDB 为物化视图提供持久化状态,让查询保持低延迟。
  • 适度扩展:当业务对窗口、联结、机器学习或大规模状态有更高要求时,仍然可以在 Kafka 之上引入 Flink、Spark 等流处理引擎,实现 “SQL + 计算引擎” 的混合模式。

通过合理评估业务需求与技术特性,你可以决定是 仅使用 ksqlDB,还是 结合外部流处理引擎,从而在成本、复杂度与性能之间找到最佳平衡点。

介绍

流处理引擎解决了一个真实的问题:对无限数据进行连续计算。Flink、ksqlDB 和 Kafka Streams 为团队提供了一种无需编写自定义消费者即可对事件流运行类 SQL 查询的方式。

该解决方案的运营成本已被广泛认可。Confluent 的官方文档指出,Flink “在部署和集群运维方面存在困难,例如调优性能或解决检查点失败”,并且“使用 Flink 的组织往往需要专门的专家团队来开发和维护”。

对于团队对 Kafka 数据提出的大量问题,存在一种更简单的架构:在对象存储的不可变分段上进行 SQL 查询

常见的 Kafka 查询

在生产环境的调试会话和运维审查中,问题往往是重复的:

  • 这个 topic 现在有什么数据?
  • 在某个故障窗口期间发生了什么?
  • 这条带有特定 key 的消息在哪里?
  • 所有分区仍在产生数据吗?

这些并不是流处理问题,而是对历史数据的有界查询。它们只运行一次,随后结束,不需要窗口、watermarks、checkpoints 或状态恢复。

Kafka的存储模型

  • Kafka 并不单独持久化每条记录,而是将它们追加到日志段(log segments),并根据大小或时间滚动这些段。
  • 每个分区是一系列有序、不可变的记录。段一旦关闭,就不可再修改。
  • Kafka 维护稀疏索引,使读取器能够高效地按偏移量和时间戳定位。每个段文件都有轻量级的偏移量索引和时间戳索引,消费者可以直接跳转到特定消息位置,而无需扫描整个文件。
  • 保留策略会删除整个段;压缩则会重写段。这意味着 Kafka 数据已经像 SQL‑on‑files 数据集一样组织好,唯一的区别在于文件所在的位置。

Kafka 3.6.0 起,分层存储(tiered storage)允许这些段存放在对象存储(例如 S3)中。到 Kafka 3.9.0,该功能已达到生产就绪水平,实现了在不改变数据模型的前提下,将持久性与计算解耦。

流式引擎的开销

流式引擎为大多数查询从未使用的功能付费:

  • 分布式状态后端
  • 协调检查点
  • 水印跟踪
  • 长时间运行的集群操作

这些成本在持续聚合、连接和实时推理时是合理的,但在“显示最近的 10 条消息”这种场景下则是浪费。

生产经验

Riskified 从 ksqlDB 迁移到 Flink,指出 ksqlDB 对模式演进的严格限制使其在真实生产用例中不切实际,并且运维复杂度导致需要更多地“与系统对抗”而不是“与系统协作”。

来自 Confluent 和 Redpanda 的供应商调查显示,约 56 % 的所有 Kafka 集群的吞吐量在 1 MB/s 或以下。大多数 Kafka 使用场景是小数据,但团队却承担了大数据的运维成本。

不可变段上的查询规划

如果 Kafka 数据以不可变段的形式存储,并带有稀疏索引,查询它的方式与其他 SQL‑on‑files 工作负载相同。

查询规划步骤

  1. 将主题解析为段文件。
  2. 根据时间戳或偏移量元数据进行过滤。
  3. 只读取相关段。
  4. 应用谓词并返回结果。

没有消费者组,没有偏移提交,也没有流式作业生命周期。

示例查询

-- Last 10 messages
SELECT * FROM orders TAIL 10;
-- Time‑bounded scan
SELECT * FROM orders
WHERE ts BETWEEN '2026-01-08 09:00' AND '2026-01-08 09:05';
-- Key lookup with recent window
SELECT * FROM orders
WHERE key = 'order-12345'
  AND ts >= now() - interval '24 hours';

这些是具有 SQL 语义的索引文件访问,而不是流处理。

性能考虑

对象存储比经纪人本地磁盘慢;远程存储的延迟通常高于本地块存储。对于大多数调试和运维工作流来说,一到两秒的延迟是可以接受的,而等待数分钟来部署或重启流式作业则不可接受。

如果需要亚秒级的持续结果,请使用流式引擎。界限很明确。

成本管理

SQL 在对象存储上的主要风险是 无限扫描。对象存储的定价基于存储的数据量和 API 调用次数。

一个负责任的系统应让每个查询报告:

  • 将读取多少段
  • 将扫描多少字节
  • 估计的请求费用

没有时间限制的查询应要求明确的选择加入,使成本成为有意识的决定,而不是意外。

何时使用流处理引擎

流处理引擎仍然是以下场景的合适工具:

  • 持续聚合
  • 对实时流的连接
  • 实时评分
  • 精确一次输出

大多数 Kafka 的交互并不属于上述情况。它们是查询和检查,由于缺乏更好的接口,被迫迁移到流处理基础设施中。

结论

一旦 Kafka 数据以不可变段的形式持久化,SQL 就成为更简洁的工具。大多数团队并不需要流处理引擎来回答 Kafka 的问题;他们需要一种干净、受限的方式来查询不可变数据。对 Kafka 段使用 SQL 正好提供了这种能力。

阅读更深入的文章请访问 .

Back to Blog

相关文章

阅读更多 »

Rapg:基于 TUI 的密钥管理器

我们都有这种经历。你加入一个新项目,首先听到的就是:“在 Slack 的置顶消息里查找 .env 文件”。或者你有多个 .env …

技术是赋能者,而非救世主

为什么思考的清晰度比你使用的工具更重要。Technology 常被视为一种魔法开关——只要打开,它就能让一切改善。新的 software,...

踏入 agentic coding

使用 Copilot Agent 的经验 我主要使用 GitHub Copilot 进行 inline edits 和 PR reviews,让我的大脑完成大部分思考。最近我决定 t...