数据分层存储
Hologres从V1.3.37版本开始支持冷热数据分层存储,帮助客户科学管理数据分层。本文为您详细介绍数据分层存储的使用方式。
功能介绍
Hologres的数据存储类型分为标准存储、低频访问存储。
标准存储即全SSD热存储,是Hologres的默认存储,满足低延时、高性能访问数据需求,对于大多数使用场景而言,标准存储是最有效且最具成本效益的选择,文中简称为热存。
低频访问存储即全HDD冷存,满足低频访问数据的低成本存储需求,适用于对延迟不敏感或不常访问的超大型数据集,文中简称为冷存。
对于主从实例,建议升级到V1.3.55及以上版本开启冷存。
同时也支持表级别的冷热数据分层,可以使用Hologres提供的动态分区管理功能动态的设置分区子表的存储介质。
Hologres从 V2.1版本开始支持基于SSD的缓存加速,帮助您提高冷存访问速度,当前仅支持默认分配的缓存空间。缓存加速默认开启,预计访问性能会提高一倍以上。
前提条件
仅HologresV1.3.37及以上版本支持数据冷热分层存储,如果您的实例低于该版本,请您使用自助升级或加入Hologres钉钉交流群反馈,详情请参见如何获取更多的在线支持?。
对于低版本的包年包月的实例,升级到V1.3.37版本后,实例的低频访问存储的配额默认是0GB。
注意事项
Hologres表的存储分为Data和Meta两部分,Data为用户的数据,Meta为表的一些元数据信息。在冷热分层存储中,用户只能设置表的Data部分存储的存储介质。出于性能考虑Meta只能存储在热存储,因此即使将表的存储策略设置为冷存储,也会占用少量热存储介质,因此会产生少量的热存储费用。
为了不影响用户服务,冷热搬迁的任务优先级较低,即用户修改表Data的存储策略后,不是立即生效,Hologres会在后台异步进行数据冷热搬迁,数据搬迁耗时跟表的数据量正相关。
向冷存表新写数据时,数据会先写到热存储,后台任务会异步的搬迁至冷存储,因此会产生一定的热存储费用。
由于磁盘寻道时间的限制,不推荐使用冷存表支撑点查场景(例如Flink维度表,Serving场景等)。冷存表支持的每秒读取行数比热存表低2个数量级。
不推荐将行存表的存储策略设为冷存储介质。
创建冷热分层表
在执行建表时,您可以通过设置SET_TABLE_PROPERTY中的storage_mode
参数来指定表的数据存储策略。详情请参见建表概述。
非分区表
指定非分区表的存储策略
如果是非分区表,可以通过SET_TABLE_PROPERTY
来设置表的存储策略。支持的选项有:
hot(热存储)。
cold(冷存储)。
例如将tbl1设置为使用冷存存储,代码如下:
-- 建表时指定存储策略为冷存储
BEGIN;
CREATE TABLE tbl1 (
"id" int NOT NULL,
"name" text NOT NULL
);
CALL set_table_property('tbl1', 'storage_mode', 'cold');
COMMIT;
修改非分区表的存储介质
Hologres支持建表后修改存储介质。例如将表tbl1的存储介质改为热存存储,系统会异步的将数据搬迁到热存存储。示例如下:
-- 建表后修改存储介质为热存储
CALL set_table_property('tbl1', 'storage_mode', 'hot');
分区表
创建分区表的存储策略
分区表也可以通过SET_TABLE_PROPERTY
来设置表的存储策略,分区子表默认继承父表的存储属性,也可以分别设置父表和子表的存储策略。支持的选项有:
hot(热存储)。
cold(冷存储)。
例如将分区父表(tbl2)设置为使用冷存存储,之后该分区父表(tbl2)对应的分区子表(tbl2_v1)都会使用冷存进行数据存储,代码如下:
-- 建表时指定存储策略为冷存储
BEGIN;
CREATE TABLE tbl2(
c1 text not null,
c2 text
)
PARTITION BY LIST(c2);
CALL set_table_property('tbl2', 'storage_mode', 'cold');
create table tbl2_v1 partition of tbl2 for values in('v1');
COMMIT;
修改分区表的存储
Hologres支持在建表后修改存储介质。例如将分区父表tbl2的存储介质改为热存存储,系统会将该分区父表对应所有分区子表的数据都异步的搬迁到热存存储。示例如下:
-- 建表后修改存储策略为热存储介质
CALL set_table_property('tbl2', 'storage_mode', 'hot');
如果您需要单独设置某个分区子表使用冷存存储,则需要修改该分区子表的storage_mode
属性。示例如下:
-- 创建多个分区子表
create table tbl2_v2 partition of tbl2 for values in('v2');
create table tbl2_v3 partition of tbl2 for values in('v3');
-- 查询表属性,当前默认继承父表的热存属性
SELECT * from hg_table_storage_status('public', 'tbl2');
-- 修改分区子表为冷存存储
CALL set_table_property('tbl2_v3', 'storage_mode', 'cold');
动态管理分区表的存储介质
对于分区表,使用冷热分层存储可以有效的平衡成本和性能。常见的场景是:动态保留最近N个历史分区在热存以应对频繁的查询,之后的M个分区在冷存以节省成本,删除超过N+M的分区。结合分区动态管理功能,可以智能的实现上述场景的自动管理。更多对于动态管理分区表的信息,请参见动态分区管理。
初始化设置
演示创建一个分区父表,这个时候存储默认是热存储。假如设置每天一个分区,动态保留最近7个历史分区在热存储介质,超过7天之后的23个分区在冷存储介质,同时删掉超过该范围的分区,可以创建分区表,代码如下:
BEGIN;
CREATE TABLE tbl2(
c1 text not null,
c2 text
)
PARTITION BY LIST(c2);
CALL set_table_property('tbl2', 'auto_partitioning.enable', 'true');
CALL set_table_property('tbl2', 'auto_partitioning.time_unit', 'DAY');
CALL set_table_property('tbl2', 'auto_partitioning.num_precreate', '3');
CALL set_table_property('tbl2', 'auto_partitioning.num_hot', '7');
CALL set_table_property('tbl2', 'auto_partitioning.num_retention', '30');
COMMIT;
效果如下图所示:
修改动态管理分区表的存储介质策略
如果我们想修改热存储介质的分区策略,可以修改auto_partitioning.num_hot
的值。需要注意的是,如果修改该策略,已经置为冷存的分区子表,不会自动的变为热存。若现在是2022年7月1日,则分区表的DDL如下:
BEGIN;
CREATE TABLE tbl_p(
c1 text not null,
c2 text
)
PARTITION BY LIST(c2);
CALL set_table_property('tbl_p', 'auto_partitioning.enable', 'true');
CALL set_table_property('tbl_p', 'auto_partitioning.time_unit', 'DAY');
CALL set_table_property('tbl_p', 'auto_partitioning.num_precreate', '3');
CALL set_table_property('tbl_p', 'auto_partitioning.num_hot', '3');
CALL set_table_property('tbl_p', 'auto_partitioning.num_retention', '10');
COMMIT;
此时的数据分布情况有如下两种场景:
场景一:扩大使用热存储截止的分区策略。
如果需要将动态管理策略的热分区策略改为4个,则使用如下代码:
CALL set_table_property('tbl_p', 'auto_partitioning.num_hot', '4');
由于已经置为冷存的分区子表,不会自动的变为热存,则更改后的效果如下图所示:
场景二:减小使用热存储截止的分区策略。
如果需要将动态管理策略的热分区策略改为2个,则使用如下代码:
CALL set_table_property('tbl_p', 'auto_partitioning.num_hot', '2');
由于已经置为冷存的分区子表,不会自动的变为热存,但是需要从热存变为冷存的数据会被迁移到冷存,则更改后的效果如下图所示:
保留固定分区子表存储属性
可以通过配置子表的keep_storage
属性保留分区的冷热存储属性。最常用的场景是将某个分区保留在热存,或者是将已迁移到冷存的分区迁移回热存。例如每年双11的数据经常会被查询,那么可以将双十一当天的数据保持在热存上,以保持其性能,示例如下:
call set_table_property('tbl1_20211111', 'keep_storage', 'true');
call set_table_property('tbl1_20211111', 'storage_mode', 'hot');
若想恢复对子表的动态管理,可以使用如下代码:
call set_table_property('tbl1_20211111', 'keep_storage', 'false');
如果您需要查看当前数据库为了保留分区表有哪些表被设置了,可以使用如下代码:
SELECT
table_namespace as schema_name
,table_name
FROM hologres.hg_table_properties
WHERE property_key='keep_storage'
AND property_value='true';
请先设置
keep_storage属性
, 再设置storage_mode
属性。若父表设置了
auto_partitioning.num_retention
属性,则keep_storage
属性不会阻止分区被删除,若想阻止删除,需要设置子表的keep_alive
为true
,例如想将2022年双十一的数据设置为热存储,且不被自动删除,可以使用如下代码:BEGIN; CALL set_table_property('tbl1_20211111', 'keep_alive', 'true'); CALL set_table_property('tbl1_20211111', 'keep_storage', 'true'); CALL set_table_property('tbl1_20211111', 'storage_mode', 'hot'); COMMIT;
将冷存分区表改为动态分区表
如果您需要将一张设置了冷存属性的分区表,改为动态分区表且近7天分区设置为热存,可以使用如下方法:
初始设置
-- 建表时指定存储策略为冷存储介质
BEGIN;
CREATE TABLE tbl2(
c1 text not null,
c2 text
)
PARTITION BY LIST(c2);
CALL set_table_property('tbl2', 'storage_mode', 'cold');
create table tbl2_20230808 partition of tbl2 for values in('20230808');
create table tbl2_20230809 partition of tbl2 for values in('20230809');
create table tbl2_20230810 partition of tbl2 for values in('20230810');
... ...
create table tbl2_20230817 partition of tbl2 for values in('20230817');
COMMIT;
修改为动态分区表且近7天分区设置为热存
begin;
CALL set_table_property('tbl2', 'storage_mode', 'hot'); --设置父表为hot
CALL set_table_property('tbl2_20230810', 'storage_mode', 'cold'); --设置不需要转热的分区为cold
CALL set_table_property('tbl2_20230809', 'storage_mode', 'cold');
CALL set_table_property('tbl2_20230808', 'storage_mode', 'cold');
CALL set_table_property('tbl2', 'auto_partitioning.enable', 'true');
CALL set_table_property('tbl2', 'auto_partitioning.time_unit', 'DAY');
CALL set_table_property('tbl2', 'auto_partitioning.num_precreate', '3');
CALL set_table_property('tbl2', 'auto_partitioning.num_hot', '7');
CALL set_table_property('tbl2', 'auto_partitioning.num_retention', '10');
commit;
查询存储介质状态
可以通过调用hg_table_storage_status
函数,查询表的存储状态。hg_table_storage_status
仅显示数据存储大小,不包含Meta存储大小。使用方法如下:
SELECT * from hg_table_storage_status('<schema_name>', '<table_name>');
参数 | 描述 |
| 表的Schema名 |
| 表名 |
返回结果如下:
列名 | 内容 |
table_name |
|
hot_size | 热存储的大小,单位Byte |
cold_size | 冷存储的大小,单位Byte |
status | 状态:
|
示例如下:
-- 非分区父表
SELECT * from hg_table_storage_status('public', 'tbl1');--返回单位是Byte
table_name | hot_size | cold_size | status
------------+----------+---------------+--------
tbl1 | 145643 | 3685 | transferring
-- 分区父表
SELECT * from hg_table_storage_status('public', 'tbl2');--返回单位是Byte
table_name | hot_size | cold_size | status
-----------------+----------+-----------+--------
tbl2_2022062222 | 0 | 0 | hot
tbl2_2022062221 | 1125 | 0 | hot
tbl2_2022062220 | 1245 | 0 | hot
tbl2_2022062219 | 1358 | 0 | hot
tbl2_2022062218 | 0 | 1875 | cold
tbl2_2022062217 | 0 | 1143 | cold
tbl2_2022062216 | 0 | 1299 | cold
查询表访问频率
Hologres从V1.3.37版本开始提供日志系统表hologres.hg_table_info
来按日收集实例内表的统计信息,帮助您对实例中的表信息进行查看、分析,以针对性的做优化,详情请参见表统计信息查看与分析,我们可以通过查询该表获取冷热数据的存储量、表数据的访问频次、分区数据访问频次,以此来判断是否需要做冷热数据转换,详细查询语句如下:
非分区表
select a.table_name,
(a.total_read_count - b.total_read_count) as read_count,
(a.total_write_count - b.total_write_count) as write_count,
a.hot_storage_size
from (Select * from hologres.hg_table_info
where type='TABLE' and collect_time::DATE = CURRENT_DATE - interval '1 day') a
join
(Select * from hologres.hg_table_info
where type='TABLE' and collect_time::DATE = CURRENT_DATE - interval '${days} day') b
on a.table_name = b.table_name
order by hot_storage_size DESC;
分区表
查询所有分区:
select parent_table_name,count(*) as partition_cnt,
sum(hot_storage_size)/1024/1024/1024 as hot_size_gb
from hologres.hg_table_info
where type = 'PARTITION' and collect_time::DATE = CURRENT_DATE - interval '1 day'
group by parent_table_name
order by hot_size_gb desc;
查询特定分区访问频次:
select a.table_name,
(a.total_read_count - b.total_read_count) as read_count,
(a.total_write_count - b.total_write_count) as write_count,
a.hot_storage_size
from (select *
from hologres.hg_table_info
where type = 'PARTITION'
and parent_table_name = '${p_table_name}'
and collect_time::DATE = CURRENT_DATE - interval '1 day') a
join
(select *
from hologres.hg_table_info
where type = 'PARTITION'
and parent_table_name = '${p_table_name}'
and collect_time::DATE = CURRENT_DATE - interval '${days} day') b
on a.table_name = b.table_name
order by table_name desc;