存储引擎选择与转换
1. 时序存储引擎
MatrixDB在时序场景中,有两种常用类型的表:
类型 | 描述 |
---|---|
Heap | Heap表为传统PostgreSQL数据库提供的存储类型,又称堆表。 该类型的表支持大量并发读写、事务、索引等特性。 |
Mars | Mars表为MatrixDB自研的存储类型。 支持压缩、列存、预聚集,在分析场景中性能极高。 但要求数据按顺序插入,并且暂不支持更新和删除。 |
所以,为了最大化时序数据写入与查询分析性能,在生产环境中需要两种类型的表搭配使用。数据写入时先写入Heap表,因为:
- Heap并发写性能高
- 时序数据接入时可能在一个较小的时间窗口内乱序上报、延迟上报、同设备分批次上报,无法保证按时间递增,需要更新操作
2. 存储引擎的分区管理与转换
在MatrixDB中,使用分区技术来搭配两种表类型,按时间戳来做数据分区。热数据先写入Heap类型的热分区,在确定已接入数据不会发生修改并且不会上报更早的数据后,将Heap热分区转换为Mars冷分区。
分区时间跨度根据数据量不同而不同。比如:数据量大,分区粒度要小,可以按天或小时来分区;数据量小,分区粒度可以调大,按周或月来分区。
MatrixDB提供了分区管理的UDF,需要先建立相关扩展组件:
CREATE EXTENSION matrixts;
CREATE EXTENSION mars;
以记录磁盘读写量为例,做冷热分级存储的用法如下:
2.1 建立时序表
CREATE TABLE disk(
time timestamp with time zone,
tag_id int,
read float,
write float
)
Distributed by (tag_id)
Partition by range(time);
通过如上SQL语句,建立了一张按时间戳分区的磁盘读写时序表,分布键是tag_id。
2.2 建立分区模板
建立好时序表后,下面通过调用mars扩展提供的build_timeseries_table
函数建立分区模板:
SELECT mars.build_timeseries_table('disk','tagkey="tag_id", timekey="time", timebucket="1 day"');
在建立分区模板的过程中,需要提供tag键和时间戳的列名,还有时间单位。
分区模板表不会在DROP原表的时候自动删除,需要手动清理。如果要删除原表,正确操作如下:
SELECT mars.destroy_timeseries_table('disk'); DROP TABLE disk;
2.3 创建分区
建立好分区模板,下面调用mars扩展提供的add_partition
函数建立分区:
SELECT mars.add_partition('disk', '2021-04-01', '2021-05-01','1 day');
如上SQL建立了从2021-04-01到2021-05-01时间段的分区,时间单位为1天。
创建好分区,调用list_partition
函数查看分区信息:
ymatrix=# SELECT * from mars.list_partition('disk');
relname | storage
-----------------------------------------------+---------
heap_disk_20210401000000000_20210402000000000 | heap
heap_disk_20210402000000000_20210403000000000 | heap
heap_disk_20210403000000000_20210404000000000 | heap
heap_disk_20210404000000000_20210405000000000 | heap
heap_disk_20210405000000000_20210406000000000 | heap
heap_disk_20210406000000000_20210407000000000 | heap
heap_disk_20210407000000000_20210408000000000 | heap
heap_disk_20210408000000000_20210409000000000 | heap
heap_disk_20210409000000000_20210410000000000 | heap
heap_disk_20210410000000000_20210411000000000 | heap
heap_disk_20210411000000000_20210412000000000 | heap
heap_disk_20210412000000000_20210413000000000 | heap
heap_disk_20210413000000000_20210414000000000 | heap
heap_disk_20210414000000000_20210415000000000 | heap
heap_disk_20210415000000000_20210416000000000 | heap
heap_disk_20210416000000000_20210417000000000 | heap
heap_disk_20210417000000000_20210418000000000 | heap
heap_disk_20210418000000000_20210419000000000 | heap
heap_disk_20210419000000000_20210420000000000 | heap
heap_disk_20210420000000000_20210421000000000 | heap
heap_disk_20210421000000000_20210422000000000 | heap
heap_disk_20210422000000000_20210423000000000 | heap
heap_disk_20210423000000000_20210424000000000 | heap
heap_disk_20210424000000000_20210425000000000 | heap
heap_disk_20210425000000000_20210426000000000 | heap
heap_disk_20210426000000000_20210427000000000 | heap
heap_disk_20210427000000000_20210428000000000 | heap
heap_disk_20210428000000000_20210429000000000 | heap
heap_disk_20210429000000000_20210430000000000 | heap
heap_disk_20210430000000000_20210501000000000 | heap
(30 rows)
如上SQL列出来了disk表包含的所有分区子表,包括子表名和存储类型。
2.4 时序数据写入表
insert into disk values(now(), ...., ..., ...);
因为表是按时间分区的,所以直接向主表插入数据即可,数据会自动落到对应的分区上。
2.5 分区替换
当4月1日的时序数据采集完毕后,即不会再有4月1日的时序数据接入,要将4月1日的数据从热分区转换为冷分区。
Mars扩展提供了compress_partition函数,用来做分区转换:
ymatrix=# select mars.compress_partition('heap_disk_20210401000000000_20210402000000000');
compress_partition
-----------------------------------------------
mars_disk_20210401000000000_20210402000000000
(1 row)
如上SQL将4月1日对应的分区做了转换,转换后再调用list_partition
函数,可看到转换后的分区表名和存储类型:
ymatrix=# SELECT * from mars.list_partition('disk');
relname | storage
-----------------------------------------------+---------
heap_disk_20210402000000000_20210403000000000 | heap
heap_disk_20210403000000000_20210404000000000 | heap
heap_disk_20210404000000000_20210405000000000 | heap
heap_disk_20210405000000000_20210406000000000 | heap
heap_disk_20210406000000000_20210407000000000 | heap
heap_disk_20210407000000000_20210408000000000 | heap
heap_disk_20210408000000000_20210409000000000 | heap
heap_disk_20210409000000000_20210410000000000 | heap
heap_disk_20210410000000000_20210411000000000 | heap
heap_disk_20210411000000000_20210412000000000 | heap
heap_disk_20210412000000000_20210413000000000 | heap
heap_disk_20210413000000000_20210414000000000 | heap
heap_disk_20210414000000000_20210415000000000 | heap
heap_disk_20210415000000000_20210416000000000 | heap
heap_disk_20210416000000000_20210417000000000 | heap
heap_disk_20210417000000000_20210418000000000 | heap
heap_disk_20210418000000000_20210419000000000 | heap
heap_disk_20210419000000000_20210420000000000 | heap
heap_disk_20210420000000000_20210421000000000 | heap
heap_disk_20210421000000000_20210422000000000 | heap
heap_disk_20210422000000000_20210423000000000 | heap
heap_disk_20210423000000000_20210424000000000 | heap
heap_disk_20210424000000000_20210425000000000 | heap
heap_disk_20210425000000000_20210426000000000 | heap
heap_disk_20210426000000000_20210427000000000 | heap
heap_disk_20210427000000000_20210428000000000 | heap
heap_disk_20210428000000000_20210429000000000 | heap
heap_disk_20210429000000000_20210430000000000 | heap
heap_disk_20210430000000000_20210501000000000 | heap
mars_disk_20210401000000000_20210402000000000 | mars
(30 rows)
可以看到,转换后的分区名前缀从heap变成了mars,存储类型也发生了相应的变化。旧的分区会被自动删除。
compress_partition还有一个时间跨度的可选参数,可以将该分区与之前mars分区做归并。如下所示,系统会根据时间跨度和当前分区所属日期自动计算,将该分区转换后归并到之前的分区,或者创建一个新的分区。
SELECT mars.compress_partition('heap_disk_20210401000000000_20210402000000000', '10 days');
3. 直接使用Mars引擎
Mars引擎不仅仅可以搭配在分区表中,也可以单独创建使用。
当用户有一批静态数据需要使用Mars引擎的高压缩、高性能优势来优化查询的时候,可以手动创建Mars表,并将数据排序后导入。
先创建扩展:
CREATE EXTENSION matrixts;
CREATE EXTENSION mars;
再创建mars表,建表时使用USING Mars
来指定存储引擎:
CREATE TABLE disk_mars(
time timestamp with time zone,
tag_id int,
read float,
write float
)
USING Mars
WITH (tagkey="tag_id", timekey="time", timebucket="8 hours")
Distributed by (tag_id);
WITH关键字中指定了Mars表的meta信息,包括:
- tag_id:设备ID字段
- timekey:时间戳字段
- timebucket:每个行组(Row Group)的时间跨度
3.1 压缩存储
Mars表支持列式压缩。压缩不仅可以减少存储量,同时也能降低查询成本。
Mars表支持4种压缩类型:
- rle_type
- zstd
- zlib
- lz4
创建Mars表时,通过compresstype指定列的压缩类型。同时还要通过compresslevel指定压缩级别(0-9),级别越高,压缩率就越高,占用空间更小,但对计算性能的消耗也更大。
如下sql创建的Mars表,为不同的列指定了不同的压缩类型:
CREATE TABLE disk_mars (
time timestamp with time zone ENCODING (compresstype=rle_type, compresslevel=1),
tag_id int ENCODING (compresstype=zstd, compresslevel=1),
read float ENCODING (compresstype=zlib, compresslevel=1),
write float ENCODING (compresstype=lz4, compresslevel=1)
)
USING Mars
WITH (tagkey="tag_id", timekey="time", timebucket="8 hours")
Distributed by (tag_id);
3.2 向Mars表导入数据
Mars表要求数据按tag_id和时间戳排序后再灌入,否则会导致查询结果错误。
将heap表数据导入到Mars表:
INSERT INTO disk_mars SELECT * FROM disk_heap ORDER BY tag_id, time;
也可以将外部表数据导入到Mars表:
INSERT INTO disk_mars SELECT * FROM ext ORDER BY tag_id, time;