关于 YMatrix
部署数据库
使用数据库
管理集群
最佳实践
高级功能
高级查询
联邦查询
Grafana 监控
备份恢复
灾难恢复
图数据库
管理手册
性能调优
故障诊断
工具指南
系统配置参数
SQL 参考
MARS3 是 YMatrix 自研的 LSM-Tree 架构存储引擎,采用行列混存架构,在传统 LSM 的基础上引入先行后列的双存储路径,既继承了行存对写入友好的特性,又保留了列存对分析查询的高性能,支持编码链压缩、数据更新删除、MVCC 机制、Brin 索引和行列混存等功能,能够同时满足 AP 和 TP 场景的需求。
MARS3 支持通过 UPDATE(Unique Mode 模式除外) 与 DELETE 子句实现数据更新与删除。
MARS3 支持增删列,支持 COPY、pg_dump 操作。
对于每个 MARS3 单表而言,其内部均采用 LSM Tree 结构存储。LSM Tree(Log Structured Merge Tree)是一种分层的,有序的,面向磁盘的数据结构。其核心思想是充分利用磁盘性能进行批量的顺序写操作,性能远高于随机写。
MARS3 内部原理图如下:
_1688719823.png)
MARS3 中存储的数据是有序的,一段连续有序的数据我们称为 Run。
Run 分成行存 Run 和列存 Run 两种。一种是为了能够高速写入,插入的数据会以行存 Run 的形式储存下来,一种是为了方便读取和压缩,把行存 Run 转换成列存 Run。
单个 Run 有大小上限:
max_runsize,在建表时用于指定单个 Run 的最大大小,最大 16384 MB;4096 MBmatrixts_internal.mars3_files 函数用来查看 MARS3 表的扩展文件和增量文件。select * from matrixts_internal.mars3_files('test');
主要包括 DATA、LINK、FSM、DELTA,如果表上有索引,还会有相应的 INDEX 文件。
DATA:主要的数据文件,用于存储用户数据。FSM(Free Space Map):在 YMatrix 中,更新和删除操作并不是对原有的数据空间进行操作,而是通过对元组的多版本形式来实现的,因此会导致“过期数据”的问题,即当一个版本的元组对所有事物都不可见时,那么它就是过期的,此时其占用的空间是可以被释放的,FSM 文件用于追踪这些可用空间,并在需要的时候能够高效地分配出去。LINK:在更新和删除操作中,用于维护 compact 中元组版本上下游关系的信息。DELTA:用于存储删除信息,MARS3 的更新和删除操作都不是采用原地修改数据的方式,而是依靠 DELTA 文件 (XMAX 等删除信息) 和版本信息屏蔽掉了老数据,从而控制数据的可见性。INDEX 和 INDEX_1_TOAST:用于存储索引文件,当前 MARS3 支持 BRIN 和 BTREE 索引。MARS3 基于 LSM-TREE 组织数据,各个 Run 文件被组织到 Level 中,最大可有 10 层:L0,L1,L2......L9。
每一层的 Run 个数达到一定数目,或者同一层多个 Run 的大小总和达到阈值都会触发合并,合成一个 Run 后升级到更高层去;并且为了加快 Run 的升级,允许同一层中同时进行多个合并任务。

在 YMatrix 中,有一定数量的后台合并进程会周期性地检测各个表的状态,并执行合并操作。
YMatrix 提供了 matrixts_internal.mars3_level_stats 实用函数用于查看 MARS3 表中每个层级的状态。
select * from matrixts_internal.mars3_level_stats('test') limit 10;
此操作对于评估表的健康状况非常有用,比如检查 Runs 是否按预期合并,是否有过多不可见的 Runs,以及运行次数 Run counts 是否在正常范围内。
按照经验法则:
level =0 时,若 runs 数大于 3,则状态不健康。level = 1 时,若 runs 数大于 50,则状态不健康。level > 1 时,若 runs 数大于 10,则状态不健康。columnstore 都是直接写直接读,没有类似 Shared Buffers 这样的缓冲层,也没有页面刷新。
每 compress_threshold (默认为 1200) 行的数据,我们称为一个 Range;一个 Range 内的某一列数据 (包含 compress_threshold 行),称之为 Stripe。
这一列数据如果特别大,那么 Stripe 会切成若干个 1MB 的 chunk,读的时候也不会一下读整个 compress_threshold 的数据出来。
RUN
└── range(按行切分,默认每 1200 行一个range)
├── column1 stripe(1200 个 datum)
├── column2 stripe(1200 个 datum)
├── column3 stripe(1200 个 datum)
└── ...
INSERT 写入到内存中,再写入 L0 的 Run 中。prefer_load_mode 和 rowstore_size 共同决定,详见下文配置项。:Normal:表示正常模式,新写入数据先写到 L0 层的行存 Run 中,积累到 rowstore_size 之后,落至 L1 层的列存 Run,相对于 Bulk 模式会多一次 I/O,列存转换由同步变成了异步,适用于 I/O 能力充足且对延迟敏感的高频次小批量写入场景;Bulk:批量加载模式,适用于低频大批量写入场景,直接写至 L1 层的列存 Run,相对于 Normal 模式,减少了一次 I/O,列存转换由异步变成了同步,适用于 I/O 能力不足且对延迟不敏感的低频大批量的数据写入Single:数据直接插入到 rowstore,元组被直接放置在 Shared Buffers 中。更多详细内容可以参照写路径总览。
目前 MARS3 目前支持 BRIN 和 BTREE 索引。
注意!
对于 MARS3 表,当前一个表上最多允许 16 个索引 (不管是否是同一个列,不管是 BRIN 还是 BTREE)。 MARS3 表仅支持mars3_brin与mars3_btree类型的索引;当前不支持在 MARS3 表上使用CREATE INDEX CONCURRENTLY。
BRIN 索引
MARS3 支持创建 mars3_brin 与 mars3_default_brin 索引,支持 Brin 索引的删除和新增。
每个 Run 在生成的时候都会创建自己独立的 Brin 索引文件。
Default Brin 是 MARS3 存储引擎的一项重要功能,在表级别提供了默认的 BRIN 索引支持,无需手动创建索引;与常规的 CREATE INDEX USING BRIN 不同 (只能索引扫描受益),顺序扫描也可以从 Default Brin 中受益,大幅提升查询效率,注意 Default Brin 不会占用索引槽的数量,即就算有 Default Brin,也依旧可以创建最多 16 个索引。
| mars3_brin | mars3_default_brin | |
|---|---|---|
| 创建方式 | 需要手动创建 | 自动创建,无需手动操作 |
| 查询支持 | 仅支持 IndexScan 时过滤数据 | 支持Indexscan、SeqScan 时过滤数据 |
| 技术版本 | brinV2 | brinV2 |
| 参数化查询 | 支持参数化查询 (param-IndexScan) | 支持参数化查询 (param-SeqScan) |
BTREE 索引
BTREE 索引是一种基于平衡多叉树结构的通用型索引,通过按键值有序组织索引节点,实现对单行或小范围数据的快速精确定位,查询复杂度稳定在 O(logN),既支持等值查询,也高效支持范围扫描与排序操作。由于其不依赖数据物理分布特性,BTREE 在高并发事务处理场景下具备极强的稳定性,是主键、唯一约束及高选择性查询的默认首选索引类型,但是不适用于低选择性列和大表的宽范围扫描。
mars3btree 是 MARS3 存储引擎里专用的 B-tree 实现,索引的内部页仍是标准 B-tree 页面,mars3btree 支持两种类型:
NORMAL:标准行式 B-tree (用于RowStore),不压缩COMPRESSED:列式压缩 B-tree (用于ColumnStore),压缩排序键是决定引擎能否发挥扫描效率、能否长期稳定运行的核心设计点。有序数据 + 可靠的块级元数据可以大幅提升扫描效率,排序键选得好,数据在 Run 内以及更高层级中会呈现更强的局部性,查询的过滤条件更容易命中连续范围,跳读更有效;排序键选得不合理,数据分布会更“散”,过滤条件无法收敛扫描范围,系统会表现为“看似有索引/有元数据,但读起来还是像全扫”。
更多有关排序键的详细内容及选型原则可查看排序键与数据局部性
从 6.8.2 版本开始,MARS3 默认使用类型化编码器进行数据压缩;如果建表时不指定 compresstype,即使用该方式。也可以通过 compresstype=auto 显式指定使用类型化编码器。类型化编码器会根据字段类型以及每个数据块的数据特征,动态选择最合适的编码方式,无需额外干预。
可以通过 automode 参数指定类型化编码器的优先策略:
automode=1:压缩效果更好。automode=2:编解码速度更好。默认值为 2。类型化编码器具备如下特点:
lz4、zstd 等压缩算法更高的压缩比。类型化编码器当前仅支持以下类型:
smallint、integer、bigintdate、time、timestamp、timestamptztext、varcharnumeric,仅限字段上定义了固定精度的情况float对于其他类型,MARS3 会采用 zstd level 3 进行压缩。
更多有关压缩对性能影响可参考压缩与性能影响;
DELETE 进行删除,删除会在对应 Run 的 Delta 文件中进行记录,在进行 Run 合并的时候真正把数据删除。UPDATE 进行更新,更新会先删除原本数据,再重新插入一条新数据。DELETE。更新无需显式使用 UPDATE 子句,直接执行 INSERT 子句即可自动完成操作。如果想要更新某个 Unique Key(即建表时指定的排序键所对应的具体键值)对应的某条数据,直接插入一条相同 Unique Key 的新数据即可。例如 CREATE TABLE mars3_t(c1 int NOT NULL, c2 int) USING MARS3 WITH (uniquemode=true) ORDER BY (c1, c2);,其中 Unique Key 即为 (c1, c2)。注意!
如开启 Unique Mode,则ORDER BY子句的第一个字段在定义时需要添加NOT NULL约束。
更多技术细节可查看更新与删除
更多技术细节可查看后台治理
Dead 的数据,当然你也可以有计划地定期使用 VACUUM 清理已 Dead 的数据。MARS3 Bucket 是 YMatrix 数据库针对 MPP 架构中并行扫描场景设计的存储层并行执行优化机制 ,通过在数据落盘阶段就按分布键哈希组织为多个逻辑桶,确保并行扫描时相同分布键的数据由同一工作进程处理,从而保留数据分布语义(locus),避免不必要的数据重分布(Motion),实现从"扫得更快"到"算得更本地"的性能跃迁。
create table foo (c1 int, c2 int) using mars3 with (mars3options='nbuckets = 2');
nbuckets 有效值: 1 ~ 128,默认值为 1,说明只有 1 个桶,即不进行分桶。建议只在有并行扫描需求的大表上启用;Bucket 依赖 Hash 分布语义,对于非 Hash 分布表没有实际收益;Unique Mode 和 continuous view 不支持 Bucket 模式。修改 nbuckets 属于重写级变更,建表时应尽量一次确定。
更多技术细节可查看 MARS3 Bucket 技术详解
在已创建 matrixts 扩展的前提下,最简洁的建表方式,只需要在 CREATE TABLE 语句加上 USING 子句,并附加 ORDER BY 子句。延伸示例请见表设计最佳实践。
=# CREATE TABLE metrics (
ts timestamp,
dev_id bigint,
power float,
speed float,
message text
) USING MARS3
ORDER BY (dev_id,ts);
注意!
MARS3 表支持创建 Brin 索引,但非必须创建;
从 6.3.0 版本开始,MARS3 表建表时去除必须使用ORDER BY子句指定排序键的使用限制。但在生产环境中,仍建议结合主要查询条件显式设计排序键;如果启用 Unique Mode,则必须提供合法的排序键。
注意!
MARS3 的表级配置分为两类:一类直接写在外层WITH (...)中,另一类写在内层mars3options='a=1,b=2,...'字符串中。两类参数不能混用,例如compresstype、compress_threshold、automode、uniquemode属于外层WITH参数,而rowstore_size、prefer_load_mode、nbuckets属于mars3options。更多信息请见 数据表配置参数。
外层 WITH 常用参数如下。
| 参数 | 单位 | 默认值 | 取值范围 | 描述 |
|---|---|---|---|---|
compresstype |
auto(6.8.2 及之后) |
none / auto / zstd / zlib / lz4 / rle_type / mxcustom |
表级压缩方式。auto 表示使用类型化编码器;mxcustom 需要配合 encodechain 使用。 |
|
compresslevel |
0 | 0 ~ 19 | 压缩级别。不同压缩算法的有效级别可能不同,例如 zlib 通常为 1 ~ 9。 |
|
compress_threshold |
元组 | 1200 | 1 ~ 65535 | 压缩阈值。用于控制每个 Range 中每列最多多少个元组一起压缩。过低时压缩收益有限,过高时会增加内存消耗。 |
encodechain |
空 | 编码链字符串 | 仅在 compresstype=mxcustom 时使用,用于指定自定义编码链。 |
|
automode |
2 | 1 / 2 | compresstype=auto 时的策略。1 优先压缩效果,2 优先编解码速度。 |
|
object_size |
MB | 64 | 正整数 | 对象大小参数,通常无需手动调整。 |
use_table_encoding |
0 | 0 / 1 | 是否优先使用表级编码配置。通常保持默认值。 | |
uniquemode |
false |
true / false |
是否启用 Unique Mode。启用后,排序键代表唯一键;同一唯一键的新写入会覆盖旧版本。 |
mars3options 常用参数如下。
| 参数 | 单位 | 默认值 | 取值范围 | 描述 |
|---|---|---|---|---|
prefer_load_mode |
normal |
normal / bulk / single |
数据加载模式。normal 为默认混合策略;bulk 更适合低频大批量写入;single 直接写入 rowstore,适合低延迟小批量写入。 |
|
rowstore_size |
MB | 64 | 8 ~ 4096 | 用于控制 L0 RowStore Run 何时切换。当数据大小超过该值,将切换到下一个 Run。 |
max_runsize |
MB | 4096 | 64 ~ 16384 | 单个 Run 的最大大小。 |
level_size_amplifier |
8 | 1 ~ 1000 | Level 尺寸的放大系数。其值越大,写入触发合并越少,但读放大风险更高;其值越小,合并更积极,但后台 IO 压力更高。 | |
default_brinkeys |
30 | -1 ~ 最大列号 | 控制 Default BRIN 覆盖的列。0 表示不启用;-1 表示系统尽量为所有支持比较的列启用;N 表示为前 N 个支持比较的列启用。 |
|
nbuckets |
1 | 1 ~ 128 | 分桶数量。用于并行扫描场景下保留分布语义。最佳实践请参考表设计与数据分布最佳实践中的“MARS3 Bucket 最佳实践”章节。 |
prefer_load_mode 的使用建议如下。
| 模式 | 适用场景 | 注意事项 |
|---|---|---|
normal |
默认模式,适合高频小批量和持续写入场景。 | 写入结束时会根据缓存大小决定继续保留 RowStore 还是刷成 ColumnStore,兼顾延迟和分析效率。 |
bulk |
低频、大批量导入,且更看重 ColumnStore 压缩和 AP 查询效率。 | 占用内存更多,单次写入延迟通常高于 normal / single。 |
single |
对单行或小批量写入延迟敏感的场景。 | 数据优先进入 RowStore,压缩、BRIN 和 AP 查询效率通常不如 ColumnStore;大批量写入后建议关注后台合并或执行 VACUUM。 |
配置示例:
=# CREATE EXTENSION matrixts;
CREATE TABLE t(
time timestamp with time zone,
tag_id int,
i4 int4,
i8 int8
)
USING MARS3
WITH (compresstype=auto, automode=2, compress_threshold=1200,
mars3options='rowstore_size=64,prefer_load_mode=normal,level_size_amplifier=8,default_brinkeys=30,nbuckets=2')
DISTRIBUTED BY (tag_id)
PARTITION BY RANGE (time)
(
START ('2026-02-01 00:00:00+08')
END ('2026-03-01 00:00:00+08')
EVERY (INTERVAL '1 day')
)
ORDER BY (time, tag_id);
matrixts_internal.mars3_level_stats:查看 MARS3 表每一个 Level 层级的状态,据此可以判断 MARS3 表的健康度,例如 Run 有没有按预期的进行合并,其个数是否符合预期等;matrixts_internal.mars3_files:查看 MARS3 表文件状态,可以用来查看 MARS3 表的扩展文件和增量文件(Data 文件、Delta 文件、Index 文件等)是不是符合预期;matrixts_internal.mars3_info_brin:查看 MARS3 表某个 Brin 索引的状态。HEAP 是 YMatrix 的默认存储引擎,又称作堆存储,从 PostgreSQL 继承而来,只支持行存储,不支持列存储及压缩。它基于 MVCC 机制实现,适用于有大量更新、删除需求的场景。
在 MVCC 机制影响下,HEAP 表在处理更新和删除操作时,并没有真正删除数据,而只是依靠数据版本信息屏蔽了老的数据(控制了数据的可见性)。因此,HEAP 表大量进行更新或删除操作,占用的物理空间会不断增大,需要你有计划地定期使用 VACUUM 清理老数据。
你可以运用以下 SQL 语句在 YMatrix 中创建一个 HEAP 表。
=# CREATE TABLE disk_heap(
time timestamp with time zone,
tag_id int,
read float,
write float
)
DISTRIBUTED BY (tag_id);
AORO 是一种面向分析型数据库的存储组织范式,指数据以仅追加(append-only)方式按行连续写入,不支持原地更新或删除,通过时间戳或事务 ID 维护版本,兼顾写入吞吐、查询效率与 MVCC 一致性。 AORO 支持行存储。
对于有大量更新及删除操作的 AO 表,同样需要计划地定期清理老数据,不过在 AO 表中,清理数据工具 vacuum 需要对 bitmap 进行重置并压缩物理文件,因此通常比 HEAP 进度慢。
注意!
存储引擎详细信息、使用及最佳实践请见表设计与数据分布最佳实践。