文章目录
一、简介
Hologres是兼容PostgreSQL协议的实时数仓引擎。Hologres在V3.1版本前支持的分区表同样兼容PostgreSQL,每个分区子表均为物理表,下文简称物理分区表。
Hologres V3.1版本起,支持逻辑分区表。逻辑分区表不仅具备物理分区表的主要优势,包括分区裁剪、分区数据管理、分区管理能力,还可有效缓解物理分区表由于表数量过多、元数据膨胀可能导致的稳定性问题。
物理分区表和逻辑分区表的部分对比如下:
| 对比项 | 物理分区表 | 逻辑分区表 |
|---|---|---|
| 分区父表 | 逻辑概念。 | 物理表。 |
| 分区子表/子分区 | 物理表。 | 逻辑概念。 |
| 元数据 | 表数量多、元数据量大、DDL数量多,FE、Query Engine压力大。 | 表数量少、元数据量小、DDL数量少,引擎层面压力小。 |
| 多级分区 | 不支持,只支持一级分区。 | 支持二级分区。 |
| 生成列做分区键 | 不支持。 | 支持,可灵活定义分区计算逻辑。 |
| 创建分区子表/子分区 | 需要预创建,存在空分区子表。 | 无需手动创建,不存在空分区。 |
| Fixed Plan写入分区父表 | 支持。 | 支持。 |
| Fixed Plan点查分区父表 | 不支持。 | 支持。 |
| 批量写入分区父表 | 不支持。 | 支持。 |
二、逻辑分区表
Hologres 自 V3.1 版本起支持逻辑分区表(LOGICAL PARTITION TABLE),即父表属于物理表,而子表为逻辑概念。本文介绍 CREATE LOGICAL PARTITION TABLE 的用法。
1、使用限制
-
仅 Hologres V3.1 及以上版本的实例支持逻辑分区表。
-
逻辑分区表仅支持
LIST分区,支持选择不超过两列的分区键。 -
逻辑分区表的分区键支持的类型为:INT、TEXT、VARCHAR、DATE、TIMESTAMP、TIMESTAMPTZ。
-
逻辑分区表的分区键需要设为非空(NOT NULL)属性。
-
逻辑分区表的分区键支持生成列。
-
逻辑分区表单表支持的分区数量上限为 5200 个,数据库级别支持的逻辑分区总数上限为 20 万个。
-
由于逻辑分区表写入数据时会自动创建对应分区,存在以下限制与建议:
-
建议优化数据写入任务和数据质量,尽可能避免脏数据(如按天分区时避免有非00:00:00时刻的数据出现),以免分区数量过度膨胀。
-
建议按分区串行写入,避免同时写入过多分区。
-
单表的导入任务有如下限制:
-
批量导入时,单个导入任务支持导入的分区数上限为50个,超出将报错
Bulkload partition count exceed limit, partition count is xxx, limit is xxx。 -
通过Fixed Plan导入时,单表同时写入的分区数超过30时,新的写入分区请求将触发流控,等待若干秒后自动提交。
-
通过Fixed Plan导入时,单表同时写入的分区数超过100时,新的写入分区请求将报错
mem partition count exceed reject limit。
-
-
为了避免单节点(Worker)未持久化分区过多导致负载过高,实例单节点有如下限制:
-
未持久化的总分区数超过500时,新的写入分区请求将触发流控,等待若干秒后自动提交。
-
未持久化的总分区数超过5000时,新的写入分区请求将报错
mem partition count exceed reject limit。 -
说明:上述"未持久化的分区"是指该节点写入到内存还未 flush 的分区个数,这里的"分区个数"并非用户分区数,而是用户分区数 × 该节点的 Shard 数 × 表的 index 个数,其中 Shard 数还包含 replica shard,因此该限制与 Shard、表在实例内部的各个节点的分布有关。
-
-
注意事项
-
不建议单分区数据量过小(如小于1亿条),否则查询加速效果不明显,且更容易生成较多小文件。可以选择更大粒度的分区。
-
如果您需要经常对某分区数据进行整体替换,包括执行 TRUNCATE 操作或 INSERT OVERWRITE 操作,建议使用分区表。针对该场景,执行 TRUNCATE 或 INSERT OVERWRITE 效果更好,可以避免大范围的删除操作。
-
TRUNCATE操作不支持生成Binlog,您需要在Session级别执行
SET hg_experimental_generate_binlog = off命令。 -
逻辑分区表无需手动创建分区,其分区是否存在取决于分区中是否有数据。当分区中数据均被删除后,分区自动删除。
说明
由于Hologres的数据清理为异步操作,因此分区也会异步删除。
-
如需修改逻辑分区表的表属性,建议使用REBUILD语法,后台会自动将任务进行拆分,并按分区串行执行。其中,如需对逻辑分区表执行Resharding操作(即修改表的Table Group),请勿使用原HG_MOVE_TABLE_TO_TABLE_GROUP存储过程,详情请参见Table Group与Shard Count操作指南。
2、创建逻辑分区表
说明
逻辑分区表拥有哪些分区,由表中的数据决定。因此,无需手动创建分区,无需删除分区。
-- 创建逻辑分区父表语句
CREATE TABLE [IF NOT EXISTS] [<schema_name>.]<table_name> ([
{
<column_name> <column_type> [ <column_constraints>, [...]]
| <table_constraints>
[, ...]
}
])
LOGICAL PARTITION BY LIST(<partition_column_1> [, <partition_column_2>])
[WITH(
<property_name> = <property_value>
[, ...]
)];
参数说明
| 参数名 | 描述 |
|---|---|
| schema_name | 表所在的Schema名称。 若是在同一个Schema下创建父表和子表,可以无需指定Schema名称。若是跨Schema创建父表和子表,则需要指定Schema名称。 |
| table_name | 需要创建的分区父表的名称。 |
| column_name | 新表中要创建的字段名。 |
| column_type | 字段的数据类型。 |
| column_constraints | 列约束的名称。 |
| table_constraints | 表约束的名称。 |
| partition_column | 逻辑分区表的分区键,支持设置1或2个。 |
| property_name | 需要对逻辑分区表设置的表属性名称。 |
| property_value | 表属性设置的值。 |
表属性说明
逻辑分区父表支持如下表属性。
说明
由于逻辑分区表的父表为物理表,分区仅为逻辑概念,因此不支持为分区单独设置下列表属性。
| 表属性 | 说明 |
|---|---|
| partition_expiration_time | 分区到期时长,到期后分区数据会异步清理。 - 默认无到期时长,数据不自动清理。 - 可设'30 day'、'12 month'等,建议该参数时间单位与分区键单位一致。 说明 - 仅支持单个分区键。 - 当分区键为时间类型(DATE、TIMESTAMP、TIMESTAMPTZ)时,无需指定partition_time_format参数。 - Hologres V4.2版本起,该属性支持分区键为TEXT类型,此时需通过partition_time_format参数指定时间格式。 |
| partition_keep_hot_window | 分区数据保持热存的时长,到期后分区数据会异步转为冷存。冷存及热存详情请参见数据分层存储。 - 默认数据全为热存储,不自动转冷。 - 可设'30 day'、'12 month'等,建议该参数时间单位与分区键单位一致。 说明 - 仅支持单个分区键。 - 当分区键为时间类型(DATE、TIMESTAMP、TIMESTAMPTZ)时,无需指定partition_time_format参数。 - Hologres V4.2版本起,该属性支持分区键为TEXT类型,此时需通过partition_time_format参数指定时间格式。 |
| partition_time_format | 分区键的时间格式,仅当分区键为TEXT类型时需要设置。Hologres V4.2版本起支持。取值如下: - YYYYMMDDHH24:小时粒度,例如2024112221。 - YYYYMMDD:天粒度,例如20241122。 - YYYYMM:月粒度,例如202411。 - YYYYQ:季度粒度,例如20241、20242、20243、20244。 - YYYY:年粒度,例如2023、2024。 - YYYY-MM-DD-HH24:小时粒度(带分隔符),例如2024-11-22-21。 - YYYY-MM-DD:天粒度(带分隔符),例如2024-11-22。 - YYYY-MM:月粒度(带分隔符),例如2024-11。 - YYYY-Q:季度粒度(带分隔符),例如2024-1。 |
| partition_require_filter | 查询父表时是否需要分区过滤条件。取值如下: - TRUE:要求分区过滤条件,否则报错。 - FALSE(默认值):不要求分区过滤条件,可以直接查询父表。 |
| binlog_level | 父表是否开启Binlog。Binlog功能详情请参见订阅Hologres Binlog。取值如下: - 'none'(默认值):不开启Binlog。 - 'replica':开启Binlog。 |
| binlog_ttl | Binlog数据生命周期,单位为秒。默认值为2592000(即30天)。 |
| partition_generate_binlog_window | 父表数据生成Binlog的时间窗口,仅在"当前时间-该参数值"范围内的分区数据会生成Binlog。 - 默认不设时间窗口,即父表的全部数据都生成Binlog。 - 可设'3 day'、'12 hour'等,建议该参数时间单位与分区键单位一致。 说明 仅支持单个分区键,且仅对时间类型的分区键生效。 |
| 索引等其他重要属性 | 逻辑分区表同样支持distribution_key、clustering_key等索引,以及orientation、time_to_live_in_seconds等其他重要属性。 属性详情请参见CREATE TABLE。您可参考场景化建表调优指南中列举的场景为索引类的表属性设置合适的值。 逻辑分区表不支持原物理分区表的动态分区管理属性,详情请参见动态分区管理。 |
分区属性说明
逻辑分区表的分区支持如下分区属性,您可通过ALTER LOGICAL PARTITION TABLE修改分区属性。
| 分区属性 | 说明 |
|---|---|
| keep_alive | 分区是否自动清理。取值如下: - TRUE:不自动清理,即不受父表的partition_expiration_time参数影响。 - FALSE(默认值):受父表的partition_expiration_time参数影响,到期自动清理。 仅父表设置了partition_expiration_time参数时生效。 |
| storage_mode | 分区是否保持某种存储类型。默认不设置,不设置时会受父表的partition_keep_hot_window参数控制。取值如下: - 'hot':分区存储类型保持热存,不受父表的partition_keep_hot_window参数控制。 - 'cold':分区存储类型保持冷存,不受父表的partition_keep_hot_window参数控制。 |
| generate_binlog | 分区是否生成Binlog。默认不设置,不设置时会受父表的partition_generate_binlog_window参数控制。取值如下: - 'on':分区会生成Binlog,不受父表的partition_generate_binlog_window参数控制。 - 'off':分区不会生成Binlog,不受父表的partition_generate_binlog_window参数控制。 |
使用示例
-
将普通列ds设置为分区键。
CREATE TABLE public.hologres_logical_parent_1 ( a TEXT, b INT, c TIMESTAMP, ds DATE NOT NULL, PRIMARY KEY (b, ds)) LOGICAL PARTITION BY LIST (ds) WITH ( orientation = 'column', distribution_key = 'b', partition_expiration_time = '30 day', partition_keep_hot_window = '15 day', partition_require_filter = TRUE, binlog_level = 'replica', partition_generate_binlog_window = '3 day' ); -
将生成列ds设置为分区键。
CREATE TABLE public.hologres_logical_parent_2 ( a TEXT, b INT, c TIMESTAMP, ds TIMESTAMP GENERATED ALWAYS AS (date_trunc('day', c)) STORED NOT NULL, PRIMARY KEY (b, ds)) LOGICAL PARTITION BY LIST (ds) WITH ( orientation = 'column', distribution_key = 'b', partition_expiration_time = '30 day', partition_keep_hot_window = '15 day', partition_require_filter = TRUE, binlog_level = 'replica', partition_generate_binlog_window = '3 day' ); -
设置两列分区键。
CREATE TABLE public.hologres_logical_parent_3 ( a TEXT, b INT, yy TEXT NOT NULL, mm TEXT NOT NULL) LOGICAL PARTITION BY LIST (yy, mm) WITH ( orientation = 'column', distribution_key = 'b', partition_require_filter = TRUE );
三、物理分区表
CREATE PARTITION TABLE语句用于创建分区表(Partition Table)。本文为您介绍CREATE PARTITION TABLE的用法。
命令说明
说明
下文无特殊说明的父表和子表皆指分区的父表和子表。
父表按分区键(Partition Key)的值划分为不同的子表,子表对外可见,分区表在使用时,需要提前创建子表。您可以通过CREATE PARTITION TABLE命令创建分区表,使用动态分区管理功能自动创建分区子表。
分区表的不同分区子表采用不同的文件存储,查询时带上分区条件,指定所需查询的分区,避免全表扫描,快速定位存储文件,提高处理效率。通常将事实表按照日期划分为不同的分区。分区表的任何一个子表在元数据存储上等同于一个非分区表,因此分区多会造成一定程度的元数据膨胀,以及小文件碎片。如果您的数据来源于数据库,不建议使用分区表。过多的分区会引起额外的IO资源浪费,为改善此问题并实现索引过滤加速查询的效果,您可以将常用分区条件作为segment_key。
1、使用限制
-
Hologres暂不支持插入数据至分区表父表,只支持插入数据至具体的分区表子表。
说明
实时计算Flink版支持实时写入数据至Hologres的分区表父表,详情请参见实时写入数据至Hologres的分区结果表。
-
一个分区规则只能创建一个分区表。
-
PARTITION BY类型仅支持LIST分区,切分PARTITION BY LIST只能取唯一值。 -
若是表有主键,分区键必须是主键的一个子集。
注意事项
-
如果单日分区数据小于1亿条,不建议使用日作为分区条件,或创建分区表。分区表太小,查询加速效果不明显,可以选择较大粒度的分区。
-
如果您需要经常对某分区数据进行整体替换,包括执行truncate操作或者drop操作,建议使用分区表。针对该场景,执行truncate或者drop效果更好,可以避免大范围的删除操作。
2、创建分区表
语法说明
创建分区表的命令格式如下。
--创建分区父表语句
CREATE TABLE [IF NOT EXISTS] [<schema_name>.]<table_name> ([
{
<column_name> <column_type> [ <column_constraints>, [...]]
| <table_constraints>
[, ...]
}
])
PARTITION BY LIST(<column_name>);
--创建分区子表语句
CREATE TABLE [IF NOT EXISTS] [<schema_name>.]<table_name> PARTITION OF <parent_table>
FOR VALUES IN (<string_literal>);
参数说明
创建分区表的参数说明如下。
| 参数 | 说明 |
|---|---|
| if not exists | 如果已经存在相同名称的表,不会抛出一个错误,而会发出一个通知,告知表关系已存在。 |
| schema_name | 表所在的schema名称,若是在同一个schema创建父表和子表,可以不需要指定schema名称。若是需要跨schema创建父表和子表,需要指定schema名称。 |
| table_name | 需要创建的分区父表或分区子表的名称。 |
| column_name | 新表中要创建的字段名。 |
| column_type | 字段的数据类型。 |
| column_constraints | 列约束的名称。 |
| table_constraints | 表约束的名称。 |
| parent_table | 子表对应的父表名称。 |
| string_literal | 分区键。 |
支持将TEXT、VARCHAR以及INT类型的数据作为分区键(Partition Key),V1.3.22及以上版本支持将DATE类型设为分区键。
使用示例
-
示例1:在public schema下创建不带主键的分区父表和对应的分区子表。
-
V2.1版本起支持的语法:
BEGIN; CREATE TABLE public.hologres_parent ( a TEXT, b INT, c TIMESTAMP, d TEXT ) PARTITION BY LIST (a) WITH (orientation = 'column'); CREATE TABLE public.hologres_child1 PARTITION OF public.hologres_parent FOR VALUES IN ('v1'); CREATE TABLE public.hologres_child2 PARTITION OF public.hologres_parent FOR VALUES IN ('v2'); CREATE TABLE public.hologres_child3 PARTITION OF public.hologres_parent FOR VALUES IN ('v3'); COMMIT; -
所有版本支持的语法:
BEGIN; CREATE TABLE public.hologres_parent( a TEXT, b INT, c TIMESTAMP, d TEXT ) PARTITION BY LIST(a); CALL set_table_property('public.hologres_parent', 'orientation', 'column'); CREATE TABLE public.hologres_child1 PARTITION OF public.hologres_parent FOR VALUES IN('v1'); CREATE TABLE public.hologres_child2 PARTITION OF public.hologres_parent FOR VALUES IN('v2'); CREATE TABLE public.hologres_child3 PARTITION OF public.hologres_parent FOR VALUES IN('v3'); COMMIT;
-
-
示例2:在public schema下创建带主键的分区父表和对应的分区子表。
V2.1版本起支持的语法:
BEGIN; CREATE TABLE public.hologres_parent_2 ( a TEXT, b INT, c TIMESTAMP, d TEXT, ds TEXT, PRIMARY KEY (ds, b) ) PARTITION BY LIST (ds) WITH (orientation = 'column'); CREATE TABLE public.holo_child_1 PARTITION OF public.hologres_parent_2 FOR VALUES IN ('20201215'); CREATE TABLE public.holo_child_2 PARTITION OF public.hologres_parent_2 FOR VALUES IN ('20201216'); CREATE TABLE public.holo_child_3 PARTITION OF public.hologres_parent_2 FOR VALUES IN ('20201217'); COMMIT;所有版本支持的语法:
BEGIN; CREATE TABLE public.hologres_parent_2( a TEXT , b INT, c TIMESTAMP, d TEXT, ds TEXT, primary key(ds,b) ) PARTITION BY LIST(ds); CALL set_table_property('public.hologres_parent_2', 'orientation', 'column'); CREATE TABLE public.holo_child_1 PARTITION OF public.hologres_parent_2 FOR VALUES IN('20201215'); CREATE TABLE public.holo_child_2 PARTITION OF public.hologres_parent_2 FOR VALUES IN('20201216'); CREATE TABLE public.holo_child_3 PARTITION OF public.hologres_parent_2 FOR VALUES IN('20201217'); COMMIT;
查看所有分区子表
您可以通过如下两种方法查看当前分区父表下所有的分区子表:
-
通过HoloWeb可视化查看,HoloWeb会展示分区父表下面的所有分区子表。
-
通过执行如下命令语句,查看当前分区父表下所有的分区子表。其中,您可以将parent_table_name修改为实际的父表名称。
SELECT nmsp_parent.nspname AS parent_schema, parent.relname AS parent, nmsp_child.nspname AS child_schema, child.relname AS child FROM pg_inherits JOIN pg_class parent ON pg_inherits.inhparent = parent.oid JOIN pg_class child ON pg_inherits.inhrelid = child.oid JOIN pg_namespace nmsp_parent ON nmsp_parent.oid = parent.relnamespace JOIN pg_namespace nmsp_child ON nmsp_child.oid = child.relnamespace WHERE parent.relname='parent_table_name';