MindsDB vs PostgresML:两条在 SQL 数据库中运行机器学习的技术路线实测对比
MindsDB vs PostgresML:两条在 SQL 数据库中运行机器学习的技术路线实测对比
导读:本文基于 PostgresML 官方博客《MindsDB vs PostgresML》(Montana Low,2023 年 6 月)整理扩充,围绕"如何用 SQL 接口在数据库中运行机器学习算法"这一核心命题,从算法能力、系统架构、端到端基准测试、云端部署四个维度,对 MindsDB 与 PostgresML 两条完全不同的实现路线进行逐项对比。读完本文,你将掌握
pgml.transform的完整调用方式、两种架构各自的技术取舍,以及为什么"数据访问方式"的差异最终会转化为 3~16 倍的推理性能差距。
为什么拿 MindsDB 与 PostgresML 做对比
市面上有很多"在 SQL 数据库中做机器学习"的方案,但绝大多数只是给机器学习套了一个 SQL 外壳,数据与计算仍然被网络分隔。MindsDB 与 PostgresML 是其中两个有代表性的开源项目,它们都试图为机器学习算法及其所需数据提供 SQL 接口,但底层思路截然不同。
这篇对比文章的直接答案是:PostgresML 更"有主见"(opinionated)、更易横向扩展、能力更全,且实测快数倍;而 MindsDB 的资历更老(按项目年龄和 GitHub Star 数计算,其成熟度约为 PostgresML 的 5 倍)。当社区反复追问"PostgresML 和 MindsDB 到底有什么区别"时,作者决定用一整篇长文把推理过程完整摊开,让读者自行判断结论是否公允。
第一眼对比:定位、许可证与实现语言
| MindsDB | PostgresML | |
|---|---|---|
| 项目年龄 | 5 年 | 1 年 |
| 许可证 | GPL-3.0 | MIT |
| 实现语言 | Python | Rust |
两者都是开源项目,但许可约束不同:PostgresML 采用更宽松的 MIT 许可证,而 MindsDB 使用 GPL-3.0。更重要的是实现语言的差异——作者刻意用了两个介词来强调:MindsDB 是"用 Python 实现"(in Python,一种带运行时的语言),PostgresML 是"用 Rust 实现"(with Rust,一种编译型、无需独立运行时的语言)。这个语言选择会在后文的架构与性能对比中反复兑现成可测量的结果。
算法能力对比:经典算法之外,数据库能力才是分水岭
两个项目都集成了几十种经典机器学习算法,包括来自 Hugging Face 的最新 LLM:
| MindsDB | PostgresML | |
|---|---|---|
| 分类 Classification | ✅ | ✅ |
| 回归 Regression | ✅ | ✅ |
| 时间序列 Time Series | ✅ | ✅ |
| LLM 支持 | ✅ | ✅ |
| Embeddings | - | ✅ |
| 向量支持 Vector | - | ✅ |
| 全文搜索 | - | ✅ |
| 地理空间搜索 | - | ✅ |
在经典机器学习算法(分类、回归、时间序列)上两者能力相当,也都通过底层 libtorch 支持从 Hugging Face 加载模型。但作者在文中对 MindsDB 的 LLM 支持做了一个重要的"划掉"修正(原文 the latest LLMs):PostgresML 只要能满足底层依赖,就可以立即使用新发布的模型;而 MindsDB 必须等官方发布更新版本才能支持新模型,其当前支持的模型范围非常有限。 新算法、新任务、新模型在持续涌现,具体支持清单应以各自最新文档为准。
真正拉开差距的是表格后半部分。PostgresML 额外支持 Embedding 模型,并把向量检索深度集成进数据库内部——这超出了 MindsDB 的能力边界,因为 MindsDB 本身根本不是数据库。PostgresML 还能直接使用其他 Postgres 扩展的全部能力:借助 pgvector 的向量索引实现高效的 KNN/ANN 向量召回,借助 PostGIS 处理地理空间信息,并内置全文搜索。多个算法与扩展可以组合进同一条复合查询,用单条 SQL 构建端到端系统——例如搜索+推荐、欺诈检测——而在传统架构里,这往往需要十几个不同的机器学习模型和微服务协作才能完成。
在仓库源码中可以印证这种"以数据库为计算中心"的设计:向量搜索的完整实现位于 pgml-sdks/pgml/src/vector_search_query_builder.rs,Embedding 生成则通过 pgml-extension/src/bindings/transformers/transformers.py 中的 embed 函数(基于 SentenceTransformer)与 rank 函数(基于 CrossEncoder)实现,并在 pgml-extension/src/api.rs 中暴露为 SQL 可调用的 pgml.embed / pgml.rank 接口。
架构对比:数据与计算在同一进程,还是隔着一层网络
两个项目在架构实现上差异巨大,这决定了它们后续所有性能与运维特性的走向:
| MindsDB | PostgresML | |
|---|---|---|
| 数据访问方式 | 网络传输(over the wire) | 进程内(in process) |
| 多进程支持 | ✅ | ✅ |
| 数据库 | - | ✅ |
| 复制 Replication | - | ✅ |
| 分片 Sharding | - | ✅ |
| 云托管 | ✅ | ✅ |
| 本地部署 | ✅ | ✅ |
| Web UI | ✅ | ✅ |
PostgresML:以 Postgres 为数据与计算的双重提供者
PostgresML 采取数据中心(data-centric)路线,让 Postgres 同时承担存储与计算。ML 算法直接运行在数据库进程内部,与数据库共享内存,通过指针直接访问数据,避免了数据密集型机器学习中最常见的序列化与网络传输开销。Rust 是这一路线的重要选择——它的内存安全特性显著降低了在大而复杂的内存空间中兼顾稳定性与高性能的难度。这条路线唯一的"代价"是:它需要一个 Postgres 数据库来承载被处理的数据。
在 pgml-extension/src/api.rs 中可以看到这种"进程内"设计在 SQL 层面的呈现——transform 被声明为 #[pg_extern(immutable, parallel_safe, name = "transform")],是一个可并行、纯函数式的数据库函数,其完整签名为:
task(JsonB):任务与模型描述;args(JsonB,默认'{}'):推理参数,如device;inputs(Array<&str>,默认ARRAY[]::TEXT[]):待处理输入;cache(bool,默认false):缓存开关(源码注释标明缓存实际由 Python 侧维护,此参数仅为 API 兼容保留)。
为了水平扩展推理能力,PostgresML 团队还开发了 PgCat 来把工作负载分发到多个 Postgres 数据库上。用分片(sharding)与复制(replication)两种策略同时扩展计算与存储——对一个低延迟、高可用的特征存储(feature store)来说,这通常是 ML 应用中最棘手的运维难题,也是 PostgresML 架构选择的首要驱动因素。PgCat 的文档与配置示例可见于 pgml-cms/docs/open-source/pgcat。
MindsDB:一个带 SQL 风格 API 的 Python 微服务
MindsDB 走的是服务导向(service-oriented)路线:它是一个相当标准的 Python 微服务架构,通过网络连接各类数据库,把数据与计算在网络两端分离——只不过它提供的接口是"看起来像 SQL"的命令,而不是 gRPC 或 REST。它接收来自特定客户端(如 psql)的伪 SQL,解析这些命令用于配置数据库连接或执行机器学习;这些命令通常带一个子查询,而子查询实际会从已配置的数据库通过网络拉取数据用于训练与推理。
所以严格来说:MindsDB 根本不是数据库,而是一个为 Python 能连上的几乎所有数据库提供适配器的 ML 服务。它的优势是生态兼容面广;代价则是引入了"数据先从数据库搬到服务进程"这条必经之路,以及一个用 Python 实现的、可能成为单点瓶颈的 ML 计算服务。分片、复制这类规模化能力,MindsDB 留给采用者自行解决。
实测基准:同一个模型,同一条数据,两种架构的差距有多大
作者指出一个前置事实:此前 PostgresML 与 Python 微服务架构的对比评测显示,PostgresML 比做同样事情的 Python 微服务架构快 8~40 倍,即便后者使用了 Redis 之类的"专用"内存数据库——网络传输与数据序列化正是数据密集型 ML 算法的两大主要成本。
由于 MindsDB 本身不提供数据库,作者设计了一个合成基准:数据不存放在数据库里,而是直接作为查询的一部分传入(虽然"SQL ML"的意义恰恰在于使用库内存储的数据,但这种设计能抵消 MindsDB 通常要付出的网络序列化与传输成本,把对比焦点完全放在 Python 与 Rust 实现本身的性能差异上)。
PostgresML:一条 SQL 搞定情感分析
连接本地 Postgres 服务器:
psql postgres://postgres:password@127.0.0.1:5432
安装扩展后无需任何额外设置,直接调用 pgml.transform 函数,把待分析文本作为 inputs 数组传入,并用 task 指定模型:
SELECT pgml.transform(
inputs => ARRAY[
'I am so excited to benchmark deep learning models in SQL. I can not wait to see the results!'
],
task => '{
"task": "text-classification",
"model": "cardiffnlp/twitter-roberta-base-sentiment"
}'::JSONB
);
结果(首次运行耗时 4769.337 ms,其中主要是模型下载与加载):
| positivity |
|---|
| [{"label": "LABEL_2", "score": 0.990081250667572}] |
首次运行 transform 时,PostgresML 会从 Hugging Face 下载该预训练 transformer 并加载进 RAM(如果有 GPU 则加载进 VRAM),所以花了约 5 秒。模型缓存后,再次调用同一个模型:
SELECT pgml.transform(
inputs => ARRAY[
'I don''t really know if 5 seconds is fast or slow for deep learning. How much time is spent downloading vs running the model?'
],
task => '{
"task": "text-classification",
"model": "cardiffnlp/twitter-roberta-base-sentiment"
}'::JSONB
);
缓存后耗时骤降至 45.094 ms,输出:
| transform |
|---|
| [{"label": "LABEL_1", "score": 0.49658918380737305}] |
45ms 低于人类感知阈值——这意味着可以用这类深度学习模型构建"感觉即时响应"的交互式应用。缓存机制在源码中有直接体现:transformers.py 用 __cache_transform_pipeline_by_task 字典以"任务键"缓存已构造的 pipeline,transform 函数按排序后的 task 键命中缓存后直接复用(同文件 transform 函数,约 L482-L500)。
GPU 与 CPU 的对比:PostgresML 会自动使用可用的 GPU。本测试机器配备 NVIDIA RTX 3090;通过给 task JSON 增加 "device": "cpu" 可强制走 CPU:
SELECT pgml.transform(
inputs => ARRAY[
'Are GPUs really worth it? Sometimes they are more expensive than the rest of the computer combined.'
],
task => '{
"task": "text-classification",
"model": "cardiffnlp/twitter-roberta-base-sentiment",
"device": "cpu"
}'::JSONB
);
CPU 耗时 165.036 ms,输出:
| transform |
|---|
| [{"label": "LABEL_0", "score": 0.7333963513374329}] |
GPU(45ms)比 24 核 i9-13900K(165ms)快约 4 倍。设备选择的自动化逻辑同样可以在源码中找到:transformers.py 的 ensure_device 函数会在 task 未指定 device/device_map 时自动探测 torch.cuda.is_available(),可用则按进程号轮询分配到 cuda:<pid % 设备数>,否则回退到 cpu。
理解模型输出:标签语义不能想当然
细心的读者会发现,三次输入的文本"情绪逐次走低",模型输出也从 LABEL_2 依次变为 LABEL_1、LABEL_0。cardiffnlp/twitter-roberta-base-sentiment 的标签含义(需查阅模型官方 README 确认):
- 0 -> Negative(负面)
- 1 -> Neutral(中性)
- 2 -> Positive(正面)
模型确实捕捉到了文本热情递减的趋势——不仅 GPU 上够快,结果也足够准确。另一个值得注意的模型质量问题是:该模型基于推文训练,而测试输入被有意设计成与推文长度和复杂度相当。模型未必总能很好地泛化到全新形态的输入,因此在挑选与验证模型输出质量时,先阅读模型文档总是必要的。
MindsDB:同一模型的对照实验
MindsDB 的搭建比"只要一个数据库"多出不少步骤。在同一台机器、同一模型、同一版本下运行:
python -m mindsdb --api postgres
然后用 Postgres 客户端连接这个 Python 服务(注意端口是 55432,与本地 Postgres 不同):
psql postgres://mindsdb:123@127.0.0.1:55432
开启计时:
\timing on
先创建模型(耗时 277.722 ms,随后后台任务下载并初始化模型,日志显示约 4 秒,但作者无法精确获得模型变为 status: complete 的时刻):
CREATE MODEL mindsdb.sentiment_classifier
PREDICT sentiment
USING
engine = 'huggingface',
task = 'text-classification',
model_name = 'cardiffnlp/twitter-roberta-base-sentiment',
input_column = 'text',
labels = ['negativ', 'neutral', 'positive'];
注意:这个 pseudo-SQL 需要用户显式配置 engine、model_name、input_column、labels 等一堆参数,模型必须"注册"成一张虚拟表才能查询。接下来用与 PostgresML 相同输入做预测(耗时 741.650 ms):
SELECT *
FROM mindsdb.sentiment_classifier
WHERE text = 'I am so excited to benchmark deep learning models in SQL. I can not wait to see the results!'
结果(MindsDB 复用了用户提供的可读标签——包括 negativ 这个拼写错误——并默认返回全部三个分数加上原始输入):
| sentiment | sentiment_explain | text |
|---|---|---|
| positive | {"positive": 0.990081250667572, "neutral": 0.008058485575020313, "negativ": 0.0018602772615849972} | I am so excited to benchmark deep learning models in SQL. I can not wait to see the results! |
尝试只返回标签、去掉 sentiment_explain 以提速(耗时 841.936 ms,反而更慢):
SELECT sentiment
FROM mindsdb.sentiment_classifier
WHERE text = 'I am so excited to benchmark deep learning models in SQL. I can not wait to see the results!'
| sentiment |
|---|
| positive |
说明瓶颈不在 sentiment_explain。作者花费数小时调试并深入了解了 MindsDB 的内部 Python 服务架构后确认了两件事:
- GPU 未生效:虽然服务启动时 Python 内部
torch.cuda.is_available()返回True,但nvidia-smi从未观察到 Python 进程使用 GPU。MindsDB 官方声称支持 GPU,但作者未能在文档或代码中找到它"不能开箱即用"的说明,只能合理假定这是一次纯 CPU 基准。 - 多进程 JSON 序列化是主要开销:Python 的 GIL 会损害并行性,MindsDB 团队因此巧妙地构建了可并行运行多个 Python 进程的服务——这对扩展有利,但代价是:查询先被序列化为 JSON 发给 worker,worker 真正运行模型后再把结果以 JSON 返回父进程。这大约就是 5 倍性能差距的来源。
结果汇总:更大的模型,差距越小,但交互场景差距显著
| task | model | MindsDB | PostgresML CPU | PostgresML GPU |
|---|---|---|---|---|
| text-classification | cardiffnlp/twitter-roberta-base-sentiment | 741 | 165 | 45 |
| translation_en_to_es | t5-base | 1573 | 1148 | 294 |
| summarization | sshleifer/distilbart-cnn-12-6 | 4289 | 3450 | 479 |
(单位为毫秒。PostgresML 数据为缓存后的单次推理耗时;MindsDB 数据为作者实测。)
存在一个普遍规律:模型越大越慢,花费在 libtorch 内部的时间占比越高,其余部分(传输、解析、调度)的性能差异就越被稀释;但对于交互式模型与交互式用例,差距依然显著。作者强调,上述对比已经尽可能选了对 MindsDB 最有利的场景——如果改用 XGBoost 等经典算法(PostgresML 中可达到亚毫秒级预测),MindsDB 仅解析传入查询就需要约 20ms 的 Python 服务开销,差距将放大到数百倍。
此外,PostgresML 以更宽松的函数式 API 支持更多模型:Hugging Face 上模型输出结构千差万别,作者尝试了多个 MindsDB 文档未列出的模型,均在创建时报错;而 PostgresML 直接透传模型的原始输出、不做结构重排,因此能容纳更多差异——代价是输出格式需要终端用户自行解析。
云部署对比:托管的是数据库,还是仅仅一个推理服务
把这类服务部署起来本身就相当费力,规模化运维更是长期投入。两家都提供云版本,差异同样源于架构路线:
- MindsDB:可在 AWS Marketplace 上基于自有的硬件实例运行,可通过 Web UI 横向扩展并配置数据源,体验与本地安装类似。但数据源方案与机器学习工作负载的扩展仍需用户自行解决——云上它依然只是一个推理服务,不附带数据库。
- PostgresML:以全托管数据库服务形式提供,包含大规模 ML 部署所需的存储、备份、监控指标与通过 PgCat 实现的扩展能力。端到端机器学习很少只是"跑模型",更多时候难在围绕模型的数据管道扩展与数据基础设施管理——这正是 PostgresML 的服务优势所在。
结论与选型建议
综合全部对比,可以得出三条可操作的结论:
- 追求端到端单查询能力,选择 PostgresML:它能用一条 SQL 完成"Embedding 生成 → 向量检索 → 经典算法/LLM 推理"的复合流程(相关构建器见 vector_search_query_builder.rs),而 MindsDB 需要额外的数据搬运与微服务编排。
- 追求吞吐与成本,选择 PostgresML:进程内数据访问消除了序列化与网络开销,实测快 3~16 倍;GPU 开箱即用(
ensure_device自动探测),经典算法可达亚毫秒级。 - 警惕"伪 SQL"的认知成本:MindsDB 需要学习其专有的
CREATE MODEL ... USING engine=...语法体系与模型注册流程,且模型支持滞后于 Hugging Face 发布节奏;PostgresML 则直接暴露标准 SQL 函数。
附:PostgresML 相关运行时配置(仓库实证)
若自行部署 PostgresML 扩展,可在 packages/postgresml/etc/postgresml/postgresml.conf 找到参考配置:
shared_preload_libraries = 'pgml,pg_stat_statements'
pgml.venv = '/var/lib/postgresml-python/pgml-venv'
与 Hugging Face 模型安全相关的 GUC 参数定义于 pgml-extension/src/config.rs,并在 whitelist.rs 的 verify_task 中执行校验:
pgml.huggingface_whitelist:允许从 Hugging Face 下载的模型清单(逗号分隔);为空则不限;pgml.huggingface_trust_remote_code:是否允许模型执行远程代码,默认false;pgml.huggingface_trust_remote_code_whitelist:允许执行远程代码的模型清单;pgml.omp_num_threads:底层 OpenMP 库使用的线程数(默认 1,仅接受正整数)。
这层白名单机制在 transform 的每个入口(api.rs)都会被调用,是部署中值得关注的安全加固点。