用SQL查MQ:Trino、PGMQ、KsqlDB、Proton、OpenLooKeng、KarelDB

概述

用SQL查MQ,算是鬼点子吧,这个方向已形成一条相当成熟的技术路线,思想大致分成三条路径:

  • 流式SQL引擎:面向实时数据流。最贴合"SQL查询MQ"思想,核心思路:把MQ的Topic当成一张可持续查询的"表",SQL不是一次性查完,而是持续订阅、不断产出结果。代表:ksqlDB、Apache Flink。
  • 联邦查询引擎:用SQL跨源查存量消息。解决需求:把MQ里堆积的历史消息,用SQL查询,常用于排查问题、追踪消息轨迹、做临时分析。代表:Trino、Apache Pulsar SQL
  • 消息过滤/检索内的SQL能力:不是"外部引擎来查MQ",而是MQ自身提供SQL风格的过滤语法,属于轻量级的内置方案。代表:RocketMQ、KarelDB

RocketMQ

在消费者订阅时支持MessageSelector.bySql(),用类SQL92的表达式(如TAGS IS NOT NULL AND age BETWEEN 18 AND 60)在Broker端过滤消息属性,相比传统Tag过滤粒度更细。实现原理:在Broker里用表达式解析对消息属性做过滤,需要开启enablePropertyFilter=true

Trino

官网,Java开发、开源(GitHub,13.1K Star,3.7K Fork)分布式SQL查询引擎,原PrestoSQL,从Presto分叉而来,本身是分布式MPP SQL查询引擎。

核心价值:联邦查询(跨源查询),能处理从GB到PB级的数据,通过连接器(Connector)机制把Kafka、Pulsar、MySQL、Hive、MongoDB等统统抽象成Catalog里的表。

配好kafka.properties后,Kafka的每条消息在Trino里就是一行,可直接SELECT * FROM kafka.default.user_events WHERE ...,支持跨源JOIN不同数据源的数据。

适合数据仓库、日志分析、数据湖查询、交互式分析、BI场景。

核心特性:

  • 无共享架构:Shared Nothing,Worker完全独立,无磁盘写依赖
  • 全内存流水线:各Operator间通过内存Page直接传递数据
  • 动态代码生成:运行时生成优化字节码,消除虚函数调用,特定数据类型特化
  • 异步I/O模型:网络与计算重叠,非阻塞数据获取,流水线气泡最小化
  • 高速查询:
  • 弹性线性扩展:
  • 低成本使用;

采用无共享架构,通过列式内存格式和动态代码生成优化性能,并提供丰富的连接器实现计算存储分离(存算分离),最大化下推优化以提升效率。

核心组件

  • Coordinator(协调器):接收SQL请求,解析生成分布式执行计划;调度Task到Worker,监控查询状态
  • Worker(工作节点):执行Task(数据扫描、过滤、聚合等操作);通过Driver驱动多个Operator(最小执行单元)
  • Connector(连接器):解耦计算与存储,通过插件支持新数据源;关键接口:getSplits()(数据分片)、getPage()。

查询执行流程:从SQL到分布式计算
Storage Metastore Worker Coordinator Client Storage Metastore Worker Coordinator Client #mermaid-svg-IHlhX2NglWkJ7zEe{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IHlhX2NglWkJ7zEe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IHlhX2NglWkJ7zEe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IHlhX2NglWkJ7zEe .error-icon{fill:#552222;}#mermaid-svg-IHlhX2NglWkJ7zEe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IHlhX2NglWkJ7zEe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IHlhX2NglWkJ7zEe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IHlhX2NglWkJ7zEe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IHlhX2NglWkJ7zEe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IHlhX2NglWkJ7zEe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IHlhX2NglWkJ7zEe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IHlhX2NglWkJ7zEe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IHlhX2NglWkJ7zEe .marker.cross{stroke:#333333;}#mermaid-svg-IHlhX2NglWkJ7zEe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IHlhX2NglWkJ7zEe p{margin:0;}#mermaid-svg-IHlhX2NglWkJ7zEe .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IHlhX2NglWkJ7zEe text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-IHlhX2NglWkJ7zEe .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-IHlhX2NglWkJ7zEe .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-IHlhX2NglWkJ7zEe .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-IHlhX2NglWkJ7zEe .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-IHlhX2NglWkJ7zEe #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-IHlhX2NglWkJ7zEe .sequenceNumber{fill:white;}#mermaid-svg-IHlhX2NglWkJ7zEe #sequencenumber{fill:#333;}#mermaid-svg-IHlhX2NglWkJ7zEe #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-IHlhX2NglWkJ7zEe .messageText{fill:#333;stroke:none;}#mermaid-svg-IHlhX2NglWkJ7zEe .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IHlhX2NglWkJ7zEe .labelText,#mermaid-svg-IHlhX2NglWkJ7zEe .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-IHlhX2NglWkJ7zEe .loopText,#mermaid-svg-IHlhX2NglWkJ7zEe .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-IHlhX2NglWkJ7zEe .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-IHlhX2NglWkJ7zEe .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-IHlhX2NglWkJ7zEe .noteText,#mermaid-svg-IHlhX2NglWkJ7zEe .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-IHlhX2NglWkJ7zEe .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IHlhX2NglWkJ7zEe .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IHlhX2NglWkJ7zEe .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IHlhX2NglWkJ7zEe .actorPopupMenu{position:absolute;}#mermaid-svg-IHlhX2NglWkJ7zEe .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-IHlhX2NglWkJ7zEe .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IHlhX2NglWkJ7zEe .actor-man circle,#mermaid-svg-IHlhX2NglWkJ7zEe line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-IHlhX2NglWkJ7zEe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 提交 SQL 查询 获取元数据 生成执行计划 分发查询任务 读取数据分片 本地计算处理 返回部分结果 结果聚合 返回最终结果

流程解读:

  • 解析与逻辑计划生成:
    • SQL Parser:将SQL文本解析成抽象语法树(AST)
    • Logical Planner:结合元数据(如表结构、统计信息),将AST转换成逻辑执行计划
  • 物理计划与分布式调度:
    • Distributed Planner:将逻辑计划拆分成多个Stage(阶段),构成一个树状依赖结构
    • Stage Scheduler:将Stage分发到各个Worker节点上执行
  • 任务执行与流水线处理:
    • Task Executor:每个Stage在Worker上表现为一或多个Task
    • Operator Pipeline:Task内部由多个Operator(操作符,如Scan、Filter、Join、Aggregation)组成

数据在这些Operator之间以"流水线"的方式传递,中间结果通常不落盘,直接在内存中流转,这是Trino速度快的重要原因。

PGMQ

项目主页即官方文档,基于PG、完全使用SQL语句实现的轻量级开源(GitHub,5.1K Star,141 Fork)MQ工具,可实现类似AWS SQS或RSMQ(Redis Simple Message Queue)的接口和功能。

功能特性

  • 轻量级:不需要后台工作进程或外部组件依赖,只需安装PG扩展插件,支持PG 14+
  • 可靠性:能够确保在可见性超时之内精确传递一次消息到消费者
  • API:兼容AWS SQS、RSMQ消息队列API
  • FIFO:遵循FIFO原则,支持消息分组和顺序处理
  • 分区队列:集成pg_partman插件按时间或ID自动分表存储消息,冷数据可自动淘汰
  • 性能提升:通过创建非日志表(create_unlogged)可极大地提高消息队列处理时间,但是可能在系统崩溃时丢失队列数据
  • 显式删除:消息可一直存储在队列中,直到明确删除为止
  • 消息重放:消息可被归档,用于长期保留和重放
  • 客户端驱动:官方提供Rust、Python驱动,社区提供Dart、Go、Elixir、Java、Kotlin、JavaScript、.NET、Ruby、PHP等语言驱动

实战

基于Docker部署:

bash 复制代码
docker run -d --name pgmq-postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 ghcr.io/pgmq/pg18-pgmq:v1.10.0

命令行使用示例:

sql 复制代码
// 创建队列
SELECT pgmq.create('my_queue');
// 发送消息
SELECT * from pgmq.send(
	queue_name => 'my_queue',
	msg => '{"foo": "bar2"}',
	delay => 5
);
// 读取消息
SELECT * FROM pgmq.read(
	queue_name => 'my_queue',
	vt => 30,
	qty => 2
);
// 弹出消息
SELECT * FROM pgmq.pop('my_queue');
// 归档消息
SELECT pgmq.archive(
	queue_name => 'my_queue',
	msg_id => 2
);
// 删除消息
SELECT pgmq.delete('my_queue', 2);
// 删除队列
SELECT pgmq.drop_queue('my_queue');

每个消息队列都对应pgmq模式下的一个表,表的命名规则为q_前缀加上消息队列名。以上语句创建的表名为pgmq.q_my_queue

消息内容使用JSON格式,查询返回的是消息编号。

发送消息时,delay可选,指定延迟时间(秒数或时间戳),在此之前该消息对消费者不可见。

读取消息时,qty指定条数,vt指定消息不可见时长。如果这些消息在vt秒内不被删除或归档,将会重新可见,且可被其他消费者读取。

弹出(POP)表示消费者读取消息之后立即从队列中删除相应的消息。

归档消息意味着从队列中删除消息,并将其插入归档表中,消息队列my_queue对应的归档表为pgmq.a_my_queue

KsqlDB

官网,前身是KSQL,基于Kafka Streams API、几乎纯Java编写、开源(GitHub,314 Star,1K Fork)实时数据流处理引擎,提供强大且易用的SQL交互方式来处理Kafka数据流,而无需编写代码。具备高扩展、高弹性、容错等优良特性,并支持多种流式处理操作,如数据过滤、转化、聚合、连接、窗口化和会话化。

提供强大的功能来处理实时数据流,适用于各种需要高效数据处理的场景。

概念

  • Event:事件,通常被称为"行",类似于关系数据库中的一行。
  • Stream:流,流是不可变的、仅可追加的历史数据集合。一旦将一行插入流中,就无法更改。流中的每一行数据存储在特定的分区中,并且每行隐式或显式地拥有一个代表其身份的键
  • Table:表,表是可变的、分区的集合,其内容会随时间而变化。表通过利用每一行的键来工作,给定键的最后一行表示该键标识的最新信息。

实战

部署形态是独立的KSQL Server进程集群,可动态扩容。

基于Docker Compose部署

yml 复制代码
version: '2'
services:
 ksqldb-server:
   image: confluentinc/ksqldb-server:0.15.0
   hostname: ksqldb-server
   container_name: ksqldb-server
   ports:
     - "8088:8088"
   environment:
     KSQL_LISTENERS: http://0.0.0.0:8088
     KSQL_BOOTSTRAP_SERVERS: 192.168.1.87:9092
     KSQL_KSQL_LOGGING_PROCESSING_STREAM_AUTO_CREATE: "true"
     KSQL_KSQL_LOGGING_PROCESSING_TOPIC_AUTO_CREATE: "true"
 ksqldb-cli:
   image: confluentinc/ksqldb-cli:0.15.0
   container_name: ksqldb-cli
   depends_on:
     - ksqldb-server
   entrypoint: /bin/sh
   tty: true

启动Docker容器并连接ksqlDB:

bash 复制代码
docker-compose up -d
docker exec -it ksqldb-cli ksql http://ksqldb-server:8088

基于Kafka的topic创建Stream,并查询Stream中的数据:

sql 复制代码
CREATE STREAM cr7_topic_stream (
 orderAmount INTEGER,
 orderId INTEGER,
 productId INTEGER,
 productNum INTEGER
) WITH (kafka_topic='cr7-topic', value_format='json');
SELECT * FROM cr7_topic_stream EMIT CHANGES;

创建Table并查询其中的数据:

sql 复制代码
CREATE TABLE cr7_topic_table (
 orderAmount INTEGER,
 orderId INTEGER,
 productId INTEGER,
 productNum INTEGER,
 kafkaProducerKey VARCHAR PRIMARY KEY
) WITH (kafka_topic='cr7-topic', value_format='json');
SELECT * FROM cr7_topic_table EMIT CHANGES;
sql 复制代码

Proton

Timeplus开源(GitHub,2.2K Star,110 Fork)KsqlDB替代方案,用C++编写、可对接多消息系统,集成ClickHouse的分析能力。

实战

基于命令行安装:

bash 复制代码
curl https://install.timeplus.com | sh

在ClickHouse里基于MergeTree引擎创建表:

sql 复制代码
CREATE TABLE events
(
	_tp_time DateTime64(3),
	url String,
	method String,
	ip String
)
ENGINE=MergeTree()
PRIMARY KEY (_tp_time, url);

https://zhuanlan.zhihu.com/p/687208908

sql 复制代码
sql 复制代码

Apache Pulsar SQL

Pulsar的数据存在BookKeeper里,本身没有表的概念;但官方直接内置Pulsar SQL,本质是打包一个Trino服务端并预装Pulsar插件,提供sql-workersql两个命令。启动后就能对Topic直接执行SQL:插件启动时通过Pulsar-Admin接口拉取Schema和分区元数据,然后创建只读BookKeeper客户端按条件过滤数据,实现"像查表一样查消息"。

OpenLooKeng

官网,曾用名OpenHetu,华为开源(GitHub,571 Star,417 Fork)使用Java开发、面向大数据库的高性能数据虚拟化引擎。提供统一SQL接口,具备跨数据源/数据中心分析能力以及面向交互式、批、流等融合查询场景。同时增强前置调度、跨源索引、动态过滤、跨源协同、水平拓展等能力。

基于SQL引擎Presto,内置Kafka等连接器,支持把Kafka Topic当表查。提供Coordinator AA高可靠、可扩展的数据源connector框架,中文文档

核心优势:

  • 跨数据中心数据分析:统一SQL接口访问跨数据中心、跨云的数据源
  • 极简的跨源数据分析体验:统一的SQL接口访问多种数据源
  • 易扩展数据源:可通过增加Connector来增加数据源采集变连接、数据零搬迁

实战

命令行安装:

bash 复制代码
wget -O - https://download.openlookeng.io/install.sh|bash

KarelDB

Java开发、几乎完全构建在Kafka之上的开源(GitHub,389 Star,27 Fork)关系数据库,MQ即存储方向的激进尝试。

支撑组件:

  • Apache Calcite:SQL引擎,负责SQL解析、优化、执行
  • Apache Omid:事务管理,MVVC支持
  • Apache Avro:序列化和模式演化(schema evolution)
  • Apache Kafka:持久化存储,使用KCache嵌入式键值存储
  • Apache Avatica:JDBC

支持SQL类型:boolean、integer、bigint、real、double、varbinary、varchar、decimal、date、time、timestamp

实战

Maven项目引入如下依赖:

xml 复制代码
<dependency>
	<groupId>io.kareldb</groupId>
	<artifactId>kareldb-core</artifactId>
	<version>1.0.0</version>
</dependency>

以内嵌模式工作:

java 复制代码
Properties properties = new Properties();
properties.put("schemaFactory", "io.kareldb.schema.SchemaFactory");
properties.put("parserFactory", "org.apache.calcite.sql.parser.parserextension.ExtensionSqlParserImpl#FACTORY");
properties.put("schema.kind", "io.kareldb.kafka.KafkaSchema");
properties.put("schema.kafkacache.bootstrap.servers", bootstrapServers);
properties.put("schema.kafkacache.data.dir", "/tmp");

try (Connection conn = DriverManager.getConnection("jdbc:kareldb:", properties);
	Statement s = conn.createStatement()) {
		s.execute("create table books (id int, name varchar, author varchar)");
		s.executeUpdate("insert into books values(1, 'The Trial', 'Franz Kafka')");
		ResultSet rs = s.executeQuery("select * from books");
		// 省略
}
相关推荐
影寂ldy15 小时前
SQL 事务(Transaction)完整版学习笔记(含转账案例+存储过程+异常捕获)
笔记·sql·学习
灯澜忆梦21 小时前
【MySQL10】进阶篇 | 索引_#1
数据库·sql·mysql
wear工程师1 天前
MySQL 分页为什么会重复数据:补上 id 还不够
sql·mysql
ClouGence1 天前
告别 Navicat!CloudDM:开源免费一站式数据库开发管理工具
数据库·sql·开源·数据库开发
xqqxqxxq2 天前
SQL 连接查询技术笔记
数据库·笔记·sql
Databend2 天前
从万亿级大模型到全线应用:Databend Cloud 助力头部 AI 企业构建全链路 Trace 数据管道
大数据·数据库·sql
ClouGence2 天前
加一张临时表,OceanBase Oracle 写入居然快了 30 倍!
数据库·sql·oracle
ClouGence2 天前
CloudDM:开源免费!一站式数据库访问、SQL审核、权限与脱敏管控平台
数据库·sql·开源