自动分区
SinceVersion 2.1
自动分区功能支持了在导入数据过程中自动检测是否存在对应所属分区。如果不存在,则会自动创建分区并正常进行导入。
使用场景
自动分区功能主要解决了用户预期基于某列对表进行分区操作,但该列的数据分布比较零散或者难以预测,在建表或调整表结构时难以准确创建所需分区,或者分区数量过多以至于手动创建过于繁琐的问题。
以时间类型分区列为例,在动态分区功能中,我们支持了按特定时间周期自动创建新分区以容纳实时数据。对于实时的用户行为日志等场景该功能基本能够满足需求。但在一些更复杂的场景下,例如处理非实时数据时,分区列与当前系统时间无关,且包含大量离散值。此时为提高效率我们希望依据此列对数据进行分区,但数据实际可能涉及的分区无法预先掌握,或者预期所需分区数量过大。这种情况下动态分区或者手动创建分区无法满足我们的需求,自动分区功能很好地覆盖了此类需求。
假设我们的表DDL如下:
CREATE TABLE `DAILY_TRADE_VALUE`
(
`TRADE_DATE` datev2 NOT NULL COMMENT '交易日期',
`TRADE_ID` varchar(40) NOT NULL COMMENT '交易编号',
......
)
UNIQUE KEY(`TRADE_DATE`, `TRADE_ID`)
PARTITION BY RANGE(`TRADE_DATE`)
(
PARTITION p_2000 VALUES [('2000-01-01'), ('2001-01-01')),
PARTITION p_2001 VALUES [('2001-01-01'), ('2002-01-01')),
PARTITION p_2002 VALUES [('2002-01-01'), ('2003-01-01')),
PARTITION p_2003 VALUES [('2003-01-01'), ('2004-01-01')),
PARTITION p_2004 VALUES [('2004-01-01'), ('2005-01-01')),
PARTITION p_2005 VALUES [('2005-01-01'), ('2006-01-01')),
PARTITION p_2006 VALUES [('2006-01-01'), ('2007-01-01')),
PARTITION p_2007 VALUES [('2007-01-01'), ('2008-01-01')),
PARTITION p_2008 VALUES [('2008-01-01'), ('2009-01-01')),
PARTITION p_2009 VALUES [('2009-01-01'), ('2010-01-01')),
PARTITION p_2010 VALUES [('2010-01-01'), ('2011-01-01')),
PARTITION p_2011 VALUES [('2011-01-01'), ('2012-01-01')),
PARTITION p_2012 VALUES [('2012-01-01'), ('2013-01-01')),
PARTITION p_2013 VALUES [('2013-01-01'), ('2014-01-01')),
PARTITION p_2014 VALUES [('2014-01-01'), ('2015-01-01')),
PARTITION p_2015 VALUES [('2015-01-01'), ('2016-01-01')),
PARTITION p_2016 VALUES [('2016-01-01'), ('2017-01-01')),
PARTITION p_2017 VALUES [('2017-01-01'), ('2018-01-01')),
PARTITION p_2018 VALUES [('2018-01-01'), ('2019-01-01')),
PARTITION p_2019 VALUES [('2019-01-01'), ('2020-01-01')),
PARTITION p_2020 VALUES [('2020-01-01'), ('2021-01-01')),
PARTITION p_2021 VALUES [('2021-01-01'), ('2022-01-01'))
)
DISTRIBUTED BY HASH(`TRADE_DATE`) BUCKETS 10
PROPERTIES (
"replication_num" = "1"
);
该表内存储了大量业务历史数据,依据交易发生的日期进行分区。可以看到在建表时,我们需要预先手动创建分区。如果分区列的数据范围发生变化,例如上表中增加了2022年的数据,则我们需要通过ALTER-TABLE-PARTITION对表的分区进行更改。如果这种分区需要变更,或者进行更细粒度的细分,修改起来非常繁琐。此时我们就可以使用AUTO PARTITION改写该表DDL。
语法
建表时,使用以下语法填充CREATE-TABLE时的partition_info
部分:
AUTO RANGE PARTITION:
AUTO PARTITION BY RANGE FUNC_CALL_EXPR
(
)
其中
FUNC_CALL_EXPR ::= date_trunc ( <partition_column>, '<interval>' )
AUTO LIST PARTITION:
AUTO PARTITION BY LIST(`partition_col`)
(
)
用法示例
AUTO RANGE PARTITION
CREATE TABLE `date_table` (
`TIME_STAMP` datev2 NOT NULL COMMENT '采集日期'
) ENGINE=OLAP
DUPLICATE KEY(`TIME_STAMP`)
AUTO PARTITION BY RANGE date_trunc(`TIME_STAMP`, 'month')
(
)
DISTRIBUTED BY HASH(`TIME_STAMP`) BUCKETS 10
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);
AUTO LIST PARTITION
CREATE TABLE `str_table` (
`str` varchar not null
) ENGINE=OLAP
DUPLICATE KEY(`str`)
AUTO PARTITION BY LIST (`str`)
(
)
DISTRIBUTED BY HASH(`str`) BUCKETS 10
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);
约束
- 自动分区的分区列必须为 NOT NULL 列。
- 在AUTO LIST PARTITION中,分区名长度不得超过 50. 该长度来自于对应数据行上各分区列内容的拼接与转义,因此实际容许长度可能更短。
- 在AUTO RANGE PARTITION中,分区函数仅支持
date_trunc
,分区列仅支持DATE
或者DATETIME
格式; - 在AUTO LIST PARTITION中,不支持函数调用,分区列支持
BOOLEAN
,TINYINT
,SMALLINT
,INT
,BIGINT
,LARGEINT
,DATE
,DATETIME
,CHAR
,VARCHAR
数据类型,分区值为枚举值。 - 在AUTO LIST PARTITION中,分区列的每个当前不存在对应分区的取值,都会创建一个独立的新PARTITION。
场景示例
在使用场景一节中的示例,在使用AUTO PARTITION后,该表DDL可以改写为:
CREATE TABLE `DAILY_TRADE_VALUE`
(
`TRADE_DATE` datev2 NOT NULL COMMENT '交易日期',
`TRADE_ID` varchar(40) NOT NULL COMMENT '交易编号',
......
)
UNIQUE KEY(`TRADE_DATE`, `TRADE_ID`)
AUTO PARTITION BY RANGE date_trunc(`TRADE_DATE`, 'year')
(
)
DISTRIBUTED BY HASH(`TRADE_DATE`) BUCKETS 10
PROPERTIES (
"replication_num" = "1"
);
此时新表没有默认分区:
mysql> show partitions from `DAILY_TRADE_VALUE`;
Empty set (0.12 sec)
经过插入数据后再查看,发现该表已经创建了对应的分区:
mysql> insert into `DAILY_TRADE_VALUE` values ('2012-12-13', 1), ('2008-02-03', 2), ('2014-11-11', 3);
Query OK, 3 rows affected (0.88 sec)
mysql> show partitions from `DAILY_TRADE_VALUE`;
+-------------+-----------------+----------------+---------------------+--------+--------------+--------------------------------------------------------------------------------+-----------------+---------+----------------+---------------+---------------------+---------------------+--------------------------+----------+------------+-------------------------+-----------+
| PartitionId | PartitionName | VisibleVersion | VisibleVersionTime | State | PartitionKey | Range | DistributionKey | Buckets | ReplicationNum | StorageMedium | CooldownTime | RemoteStoragePolicy | LastConsistencyCheckTime | DataSize | IsInMemory | ReplicaAllocation | IsMutable |
+-------------+-----------------+----------------+---------------------+--------+--------------+--------------------------------------------------------------------------------+-----------------+---------+----------------+---------------+---------------------+---------------------+--------------------------+----------+------------+-------------------------+-----------+
| 180060 | p20080101000000 | 2 | 2023-09-18 21:49:29 | NORMAL | TRADE_DATE | [types: [DATEV2]; keys: [2008-01-01]; ..types: [DATEV2]; keys: [2009-01-01]; ) | TRADE_DATE | 10 | 1 | HDD | 9999-12-31 23:59:59 | | NULL | 0.000 | false | tag.location.default: 1 | true |
| 180039 | p20120101000000 | 2 | 2023-09-18 21:49:29 | NORMAL | TRADE_DATE | [types: [DATEV2]; keys: [2012-01-01]; ..types: [DATEV2]; keys: [2013-01-01]; ) | TRADE_DATE | 10 | 1 | HDD | 9999-12-31 23:59:59 | | NULL | 0.000 | false | tag.location.default: 1 | true |
| 180018 | p20140101000000 | 2 | 2023-09-18 21:49:29 | NORMAL | TRADE_DATE | [types: [DATEV2]; keys: [2014-01-01]; ..types: [DATEV2]; keys: [2015-01-01]; ) | TRADE_DATE | 10 | 1 | HDD | 9999-12-31 23:59:59 | | NULL | 0.000 | false | tag.location.default: 1 | true |
+-------------+-----------------+----------------+---------------------+--------+--------------+--------------------------------------------------------------------------------+-----------------+---------+----------------+---------------+---------------------+---------------------+--------------------------+----------+------------+-------------------------+-----------+
3 rows in set (0.12 sec)
经过自动分区功能所创建的PARTITION,与手动创建的PARTITION具有完全一致的功能性质。
与动态分区联用
自动分区支持与动态分区同时作用于同一张表上,例如:
CREATE TABLE tbl3
(
k1 DATETIME NOT NULL,
col1 int
)
AUTO PARTITION BY RANGE date_trunc(`k1`, 'year') ()
DISTRIBUTED BY HASH(k1)
PROPERTIES
(
"replication_num" = "1",
"dynamic_partition.create_history_partition"="true",
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "year",
"dynamic_partition.start" = "-2",
"dynamic_partition.end" = "2",
"dynamic_partition.prefix" = "p",
"dynamic_partition.buckets" = "8"
);
当两种功能联用时,它们的原始功能均不受影响,依旧作用于整张表上,行为包括但不限于:
- 无论创建方式如何,过期的历史分区都会按动态分区功能指定的规则定期清理或转入冷存储
- 分区范围不能重叠、冲突。如果动态分区需要创建的新分区范围已经被自动或手动创建的分区覆盖,则该分区创建会失败,但不影响业务过程。
其原则在于,自动分区仅是对创建分区引入的一种补充手段,一个分区无论是手动、经自动分区创建,还是经动态分区创建的,均会受到动态分区的管理。
限制
为简化两种分区方式联用的行为模式,当前自动分区和动态分区联用时,两者的分区间隔必须一致,否则建表将会失败:
mysql > CREATE TABLE tbl3
(
k1 DATETIME NOT NULL,
col1 int
)
AUTO PARTITION BY RANGE date_trunc(`k1`, 'year') ()
DISTRIBUTED BY HASH(k1)
PROPERTIES
(
"replication_num" = "1",
"dynamic_partition.create_history_partition"="true",
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "HOUR",
"dynamic_partition.start" = "-2",
"dynamic_partition.end" = "2",
"dynamic_partition.prefix" = "p",
"dynamic_partition.buckets" = "8"
);
ERROR 1105 (HY000): errCode = 2, detailMessage = errCode = 2, detailMessage = If support auto partition and dynamic partition at same time, they must have the same interval unit.
注意事项
- 如同普通分区表一样,AUTO PARTITION支持多列分区,语法并无区别。
- 在数据的插入或导入过程中如果创建了分区,而整个导入过程没有完成(失败或被取消),被创建的分区不会被自动删除。
- 使用AUTO PARTITION的表,只是分区创建方式上由手动转为了自动。表及其所创建分区的原本使用方法都与非AUTO PARTITION的表或分区相同。
- 为防止意外创建过多分区,我们通过FE配置项中的
max_auto_partition_num
控制了一个AUTO PARTITION表最大容纳分区数。如有需要可以调整该值 - 向开启了AUTO PARTITION的表导入数据时,Coordinator发送数据的轮询间隔与普通表有所不同。具体请见BE配置项中的
olap_table_sink_send_interval_auto_partition_factor
。 - 在使用insert-overwrite插入数据时,如果指定了覆写的partition,则AUTO PARTITION表在此过程中表现得如同普通表,不创建新的分区。