12k
All articles

5 个值得了解的 MariaDB 特性

MariaDB值得了解的功能:向量搜索、系统版本表和应用时间表、按表选择存储引擎,以及11.8 LTS中的utf8mb4默认值。

OpenReplay Team
OpenReplay Team
5 个值得了解的 MariaDB 特性

对于正在运行 MySQL 8.0 的团队而言,MariaDB 最具影响力的五项特性分别是:内置向量搜索、系统版本化表(system-versioned tables)、应用时间周期(application-time periods)、逐表选择存储引擎,以及默认采用 utf8mb4 并搭配 UCA 14.0.0 排序规则——后者自 MariaDB 11.6 起生效,并延续到 11.8 LTS 版本线。

如果你正在为 MySQL 8.0 环境权衡版本升级方案,MariaDB 是一个值得纳入考量的选项。

下文将逐一介绍每项特性:它能做什么,以及你需要编写的 SQL。版本参考基于 MariaDB 11.8 LTS 和 MariaDB 12.3 LTS,这也是大多数团队现在着手部署时会安装的版本。

核心要点

  • MariaDB 在社区版服务器中直接提供向量相似度搜索:一个 VECTOR(N) 列和一个基于改进版 HNSW 算法构建的 VECTOR INDEX,无需安装任何扩展。
  • 只有当 ORDER BY 是一个与索引构建时所用度量方式相匹配的纯距离函数调用、且后跟 LIMIT 时,MariaDB 才会使用向量索引;若距离函数不匹配,查询将退化为全表扫描。
  • WITH SYSTEM VERSIONING 配合 SELECT ... FOR SYSTEM_TIME AS OF 可实现时间点查询,无需审计触发器或影子历史表。
  • PERIOD FOR 用于在表上声明业务有效期;同时声明系统版本化和应用时间周期,即可让该表成为双时态(bitemporal)表。
  • 自 MariaDB 11.6 起,默认字符集为 utf8mb4,排序规则基于 UCA 14.0.0,而 11.8 是首个包含该变更的长期支持版本。

MariaDB 如何在不依赖扩展的情况下实现向量搜索?

MariaDB 在社区版服务器内部就完成了嵌入向量的存储与搜索:一个 VECTOR(N) 列、一个采用改进版 HNSW 算法的 VECTOR INDEX,以及用于余弦(cosine)和欧几里得(euclidean)相似度的距离函数。MariaDB Vector 项目页面将正式可用(GA)节点定在 11.8 LTS,而 VECTOR 类型参考文档则将单列维度上限设为 16,383。无需额外安装任何东西,也不会有第二个数据存储系统与你的数据行并行存在、随时可能出现数据不同步的问题。

CREATE TABLE support_articles (
  id         BIGINT UNSIGNED PRIMARY KEY,
  tenant_id  INT NOT NULL,
  body       TEXT,
  embedding  VECTOR(768) NOT NULL,
  VECTOR INDEX (embedding) M=8 DISTANCE=cosine
) ENGINE=InnoDB;

768 对应几种广泛使用的开放权重句子嵌入模型的输出维度;实际使用时应设置为你的模型所输出的维度。这段 DDL 中有两个约束值得注意:被索引的列必须是 NOT NULL;M 的取值范围是 3 到 200,数值越高准确率越好,但代价是索引体积增大、写入速度下降——详见 create-table-with-vectors 参考文档。项目页面同时指出,每张表只能有一个向量索引。

服务器负责存储和搜索向量,但不负责生成向量。因此,向量数组来自你的嵌入模型,并以文本形式写入:

INSERT INTO support_articles (id, tenant_id, body, embedding)
VALUES (1, 17, 'Resetting a device token',
        VEC_FromText('[0.021, -0.113, 0.447, ...]'));

由于嵌入向量存放在普通的 InnoDB 表中,WHERE 子句和相似度排序可以在同一条事务性语句中执行。外挂在数据库旁边的向量服务做不到这一点:将最近邻搜索限定在单个租户的数据行范围内,既不需要跨两个系统的 join,也不需要对已经付出代价检索出来的结果做后置过滤。

SELECT id, body
FROM support_articles
WHERE tenant_id = 17
ORDER BY VEC_DISTANCE_COSINE(embedding, VEC_FromText('[...]'))
LIMIT 10;

为什么向量索引会被跳过?

只有当查询按照纯粹的距离函数调用排序、所用度量方式与索引构建时一致、并且用 LIMIT 限制返回行数时,索引才会生效。如果对一个按余弦距离构建的索引使用欧几里得距离函数查询,参考文档明确指出该索引无法服务于此查询,查询将退化为全表扫描:

-- index above was built with DISTANCE=cosine
SELECT id, body
FROM support_articles
WHERE tenant_id = 17
ORDER BY VEC_DISTANCE_EUCLIDEAN(embedding, VEC_FromText('[...]'))
LIMIT 10;

通用的 VEC_DISTANCE 函数能规避这一陷阱:它会根据底层索引自动解析为欧几里得距离或余弦距离,具体说明见向量功能概览;目前仅支持这两种度量方式。如果在较小的 LIMIT 值下召回率很重要,mhnsw_ef_search 可设定索引搜索过程中保留的候选结果数量:默认值为 20,可在 1 到 10000 之间设置。即使 LIMIT 要求的行数更少,搜索也不会低于该数量,因此调高该值能以搜索耗时为代价换取结果质量。

作为对比,MySQL 8.0 没有 VECTOR 类型;向量类型是在 9.x 系列中才引入的,而基于索引的向量搜索被记录为 HeatWave 的能力——HeatWave GenAI 会自行构建索引,针对那些被频繁查询的向量列。在 PostgreSQL 中,这项工作则交给 pgvector,一个需要你自行安装并启用的扩展。

系统版本化表:如何查询上周二的数据?

系统版本化表会把每一行被覆盖的历史版本都保留在表内部,因此时间点读取只是普通 SELECT 上的一个子句,而不需要审计触发器往你必须自行维护的历史表里写数据。由于旧版本与当前版本共存于同一张表,你可以按任意过去时刻的状态读取该表、追溯变更内容,并把两个日期的数据并排比对。

CREATE TABLE subscription (
  account_id  BIGINT PRIMARY KEY,
  plan        VARCHAR(32),
  seats       INT
) WITH SYSTEM VERSIONING;

SELECT account_id, plan, seats
FROM subscription
FOR SYSTEM_TIME AS OF TIMESTAMP '2025-11-04 09:00:00'
WHERE account_id = 4021;

这就是该特性的全部内容。只有指定了 FOR SYSTEM_TIME 才会返回历史数据,系统版本化表参考文档列出了 AS OF、BETWEEN、FROM ... TO 和 ALL 这几种查询形式。显式 DDL 写法需要声明 GENERATED ALWAYS AS ROW START 和 ROW END 列并配合 PERIOD FOR SYSTEM_TIME;上面的简写形式则把同样的信息存放在 ROW_START 和 ROW_END 伪列背后。在 InnoDB 上,你还可以使用 BIGINT UNSIGNED 类型的 row-start 和 row-end 列按事务进行版本化,并通过 FOR SYSTEM_TIME AS OF TRANSACTION 读取。MariaDB 官方的差异说明文档将时态数据表列为 MySQL 没有对应实现的能力之一。

用于业务有效期的应用时间周期

系统版本化记录的是数据库何时被告知某件事;应用时间周期记录的则是该事实实际成立的时间范围。应用时间周期由两个类型和宽度一致的时间列界定,它在应用层面而非服务器层面对数据进行版本化。带有效期窗口的价格是最典型的应用场景。

CREATE TABLE price_list (
  sku        VARCHAR(32),
  price      DECIMAL(10,2),
  valid_from DATE,
  valid_to   DATE,
  PERIOD FOR validity(valid_from, valid_to),
  UNIQUE (sku, validity WITHOUT OVERLAPS)
);

UPDATE price_list FOR PORTION OF validity
  FROM '2026-01-01' TO '2026-04-01'
  SET price = 18.00
WHERE sku = 'KB-114';

声明一个周期会强制两列均为 NOT NULL,并在后台自动添加一项检查,确保第一个值早于第二个值。在主键或唯一键上使用 WITHOUT OVERLAPS 则会拒绝周期相互重叠的数据行。该子句自 MariaDB 10.5.3 起被记录在文档中;周期本身(包括 FOR PORTION)更早发布,始于 MariaDB 10.4.3。在同一张表上同时声明周期和系统版本化,你就得到了一张双时态表,它会在同一批数据行上同时运行两种版本化机制。正是这一点,让”周二那天价格本身就是错的”和”价格只是录入得晚了”得以区分开来。

可以为每张表单独选择存储引擎吗?

MariaDB 按表设置存储引擎,因此事务型表可以留在 InnoDB 上,而同一 schema 中的其他表则可使用别的引擎。除标准引擎集之外,差异说明文档还列出了用于分布式分析处理的 ColumnStore、MyRocks、用于云端归档的 S3 引擎、作为 MyISAM 替代品的 Aria,以及 CONNECT、SEQUENCE、Spider、SphinxSE、FederatedX 和 OQGRAPH。

CREATE TABLE event_archive (
  id   BIGINT PRIMARY KEY,
  body TEXT
) ENGINE=Aria;

其中一些引擎是以独立插件包的形式发布的,并未编译进服务器本体,因此在默认一句 ENGINE= 子句就足够之前,请先查阅你所安装版本中该引擎自身的文档。

utf8mb4 默认设置与更新的排序规则

MariaDB 在 11.6 版本中把服务器默认字符集从 latin1 改为 utf8mb4,并同步将排序规则更新至 UCA 14.0.0。如果你在长期支持版本之间升级,11.8 就是这两项变更落地的版本,2025 年 6 月 8 日发布的 11.8 LTS 发布公告将其作为该版本内容的一部分予以说明。这是 MariaDB 淘汰自身一个早于 emoji 时代的默认设置,而非它在默认配置上反超 MySQL 的节点。真正在升级之后长期产生影响的是排序规则这一侧:排序顺序和比较行为遵循的 Unicode 排序算法版本比 MySQL 更新,因此如果你要对比两者的输出,交叉核对排序结果是值得做的一件事。

MariaDB 特性及其对应版本

特性你能做什么MariaDB 可用版本
向量搜索在 VECTOR(N) 中存储嵌入向量,用 VECTOR INDEX 建立索引,在一条语句中完成过滤与排序VECTOR 类型在 11.7.1 中加入,11.8 LTS 正式 GA,12.3 LTS 中同样具备
系统版本化表按过去某个时间戳或事务读取表的历史状态11.8 与 12.3 LTS 版本中可用
应用时间周期建模业务有效期窗口,并更新其中的某一段周期与 FOR PORTION 自 10.4.3 起,WITHOUT OVERLAPS 自 10.5.3 起,当前 LTS 中均具备
双时态表在同一张表上同时按系统时间和业务时间进行版本化当前 LTS 中具备
utf8mb4 默认值、UCA 14.0.0无需修改服务器配置即可使用 Unicode自 11.6 起为默认值,首个包含该变更的 LTS 是 11.8

有一条升级注意事项值得记住:11.8 的发布说明指出,只要未使用系统版本化表,TIMESTAMP 范围扩展就无需进行数据转换,并将这类表列为唯一已知的复杂情况,原因是内部时间戳表示方式发生了变化。

从这五项特性中,挑出那个能解决你手头已有问题的。如果问题是审计历史,那就创建一张用完即弃的 WITH SYSTEM VERSIONING 表,修改某一行两次,再用 FOR SYSTEM_TIME AS OF 读回来;几分钟之内你就能判断它是否可以取代你一直在维护的那个触发器。

常见问题

我能为一张已经存在的表启用系统版本化吗?

可以。ALTER TABLE t ADD SYSTEM VERSIONING 可在现有表上启用该功能,ALTER TABLE t DROP SYSTEM VERSIONING 则将其移除,并删除全部历史数据。两种操作都会重建表,因此在大表上可能很慢。后续的 schema 变更取决于 system_versioning_alter_history 设置:设为 ERROR 时,对版本化表执行 alter 会失败;设为 KEEP 时,alter 会成功,但历史查询将显示新的表结构。

如何避免系统版本化表无限膨胀?

文档给出了三种方案:使用 DELETE HISTORY 语句进行清理,这需要 DELETE HISTORY 权限;按 SYSTEM_TIME 分区并删除历史分区,但请注意你无法删除当前分区或唯一的历史分区;或者移除后重新添加系统版本化,这会清空历史,代价是一次表重建。TRUNCATE TABLE 也无法达到目的:服务器会以错误 4137 拒绝执行,以保全历史数据。

在 MariaDB 中,我能为同一个向量列同时建立余弦距离和欧几里得距离的索引吗?

不能。一个向量索引只针对单一距离函数构建,有效取值为 cosine 和 euclidean(默认),使用其他距离函数的搜索无法利用该索引。MariaDB 还规定每张表只允许一个向量索引,因此要支持第二种度量方式,只能删除现有索引并以不同的 DISTANCE 值重建,而不能在其旁边再加一个。

MariaDB 11.8 的 utf8mb4 默认设置会影响向旧版服务器的复制吗?

会。在 MariaDB 11.6 中,默认字符集从 latin1 改为 utf8mb4,默认排序规则改为 utf8mb4_uca1400_ai_ci,而 11.8 是首个包含该变更的长期支持版本。旧版本没有这个排序规则,因此除非你把服务器配置改回旧的默认值,否则 MariaDB 11.8 主库无法复制到 MariaDB 10.6 从库。

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.