自动化分区管理

本文档介绍了 YMatrix 中的自动化分区管理策略及使用。

在生产环境中,常按时间做数据分区。YMatrix 提供了一些用来管理分区的 UDF(User Defined Functions),你可以根据自己的需求手动做分区维护。如:每天一个分区,每 8 个小时一个分区等。
但时间是一个无限延伸的维度,在创建分区表的时候管理员通常只是提前将未来一段时间内的分区创建好,然后定期增加新的时间段分区。这无疑增加了运维成本。

自动化分区管理就是将手动维护分区的工作自动化,从而降低管理员维护数据库的成本。包括:

  1. 分区自动创建与删除
  2. 默认分区自动切分
  3. 批量创建分区
  4. 强制保留特定历史分区
  5. 自定义自动分区操作时段

自动化分区管理功能包含在 matrixts 扩展中,所以首先要创建 matrixts 扩展:

=# CREATE EXTENSION matrixts;

通过调用 set_policy,为目标表设置策略,来实现分区维护自动化:

目前包含如下 3 种策略:

  1. auto_partitioning
  2. auto_splitting
  3. auto_partitioning_ex

注意!
从 5.0 版本开始,YMatrix 新增了 auto_partitioning_ex 策略,建议使用此策略替代 auto_partitioning,以实现批量创建分区、强制保留特定历史分区、自定义自动分区操作时段等高级功能。

1 分区策略

1.1 auto_partitioning

auto_partitioning 策略应用于普通分区表的自动化管理,实现了如下 2 种自动化功能:

  1. 新分区创建
  2. 过期分区清理

示例,创建测试分区表 metrics,如下:

=# CREATE TABLE metrics(
    time timestamp with time zone,
    tag_id int,
    read float,
    write float
)
DISTRIBUTED BY (tag_id)
PARTITION BY RANGE (time);

为 metrics 表创建 auto_partitioning 自动分区策略:

=# SELECT set_policy(
    'metrics'::regclass,
    'auto_partitioning'
);

如果需要携带模式信息的话,传递参数 schema_name.table_name 即可。

设置好策略后,调用 list_policy 查看策略:

=# SELECT * FROM list_policy('metrics'::regclass);

 reloid | relname | class_id |    class_name     |   action    | seq | disabled |               check_func               |      check_args      |                    act_func                    |       act_args        | version
--------+---------+----------+-------------------+-------------+-----+----------+----------------------------------------+----------------------+------------------------------------------------+-----------------------+---------
  17798 | metrics |        2 | auto_partitioning | retention   |   1 | t        | matrixts_internal.apm_generic_expired  | {"after": "1 year"}  | matrixts_internal.apm_generic_drop_partition   | {}                    | 1.0
  17798 | metrics |        2 | auto_partitioning | auto_create |   2 | f        | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_partition | {"period": "8 hours"} | 1.0
(2 rows)

可以看到,auto_partitioning 总共包含两种操作:

  1. retention:过期分区删除
  2. auto_create:新分区创建

其中 retention 的检测参数是 {"after": "1 year"} ,即检测分区是否超过一年,然后删除。 auto_create 的检测参数是 {"before": "3 days"},即提前 3 天检测是否需要创建新分区;执行参数为 {"period": "8 hours"},即创建新分区的时间跨度为 8 小时。这是 auto_partitioning 策略的默认设置。

如果要调整这些参数,需要调用 set_policy_action

示例,将 retention 调整为 3 个月后自动删除;auto_create 调整为提前 2 天,时间跨度为 1 天:

=# SELECT set_policy_action(
    'metrics'::regclass,
    'retention',
    '3 months'
);

=# SELECT set_policy_action(
    'metrics'::regclass,
    'auto_create',
    '{
        "before": "2 days",
        "period": "1 day"
    }'
);

注意!
对于只有一个参数的 action 来说,直接写目标值字符串即可,如 retention。对于包含多个参数的 action,目标值需要通过 JSON 拼接完成。如 auto_create

1.2 auto_splitting

auto_splitting 策略对包含默认分区的分区表,实现默认分区按时间切割操作。

示例,创建分区表,并为之创建默认分区:

=# CREATE TABLE metrics(
    time timestamp with time zone,
    tag_id int,
    read float,
    write float
)
DISTRIBUTED BY (tag_id)
PARTITION BY RANGE (time);

=# CREATE TABLE metrics_others PARTITION OF metrics DEFAULT;

设置 auto_splitting 策略:

=# SELECT set_policy(
    'metrics'::regclass,
    'auto_splitting'
);

查看策略:

=# SELECT * FROM list_policy('metrics'::regclass);
 reloid | relname | class_id |   class_name   |   action   | seq | disabled |                check_func                |        check_args        |                       act_func                        |       act_args        | version
--------+---------+----------+----------------+------------+-----+----------+------------------------------------------+--------------------------+-------------------------------------------------------+-----------------------+---------
  17798 | metrics |        1 | auto_splitting | retention  |   1 | t        | matrixts_internal.apm_generic_expired    | {"after": "1 year"}      | matrixts_internal.apm_generic_drop_partition          | {}                    | 1.0
  17798 | metrics |        1 | auto_splitting | auto_split |   2 | f        | matrixts_internal.apm_generic_older_than | {"age": "1 month 2days"} | matrixts_internal.apm_generic_split_default_partition | {"period": "1 month"} | 1.0
(2 rows)

可以看到,auto_splitting 策略包含2种操作,过期分区删除 retention 和默认分区自动切割 auto_split

其中,{"age": "1 month 2days"} 表示当默认分区中最老的数据早于当前时间1月零2天前的时候,进行切割。{"period": "1 month"} 表示切割后分区粒度为1个月。

1.3 auto_partitioning_ex

auto_partitioning_ex 策略是在 auto_partitioning 的基础上升级功能的分区策略。其包含一些对分区表的高级管理操作:

  1. 批量创建分区
  2. 强制保留特定历史分区
  3. 自定义自动分区工作时段

以上操作将在下文示例中详述。

创建分区表,并为之创建 apmtrap 分区:

=# CREATE TABLE metrics(
    time timestamp with time zone,
    tag_id int,
    read float,
    write float
)
DISTRIBUTED BY (tag_id)
PARTITION BY RANGE (time);

=# CREATE TABLE metrics_invalid_apmtrap PARTITION OF metrics FOR VALUES FROM ('-infinity') TO ('2023-01-01');

设置 auto_partitioning_ex 策略:

=# SELECT set_policy(
    'metrics'::regclass,
    'auto_partitioning_ex'
);

查看策略:

=# SELECT * FROM list_policy('metrics');
reloid | relname | class_id |      class_name      |   action    | seq | disabled |                check_func                 |                                                               check_args
                   |                    act_func                    |       act_args        | version
--------+---------+----------+----------------------+-------------+-----+----------+-------------------------------------------+---------------------------------------------------------------------------------------------------------------------
-------------------+------------------------------------------------+-----------------------+---------
  34917 | metrics |        4 | auto_partitioning_ex | retention   |   1 | t        | matrixts_internal.apm_generic_expired     | {"after": "1 year"}
                   | matrixts_internal.apm_generic_drop_partition   | {}                    | 1.0
  34917 | metrics |        4 | auto_partitioning_ex | auto_create |   2 | f        | matrixts_internal.apm_generic_incoming_ex | {"before": "3 days", "period": "8 hours", "end_time": "00:00:00", "batch_size": "0", "start_time": "00:00:00", "stor
age_type": "heap"} | matrixts_internal.apm_generic_append_partition | {"period": "8 hours"} | 1.0
(2 rows)
  1. 批量创建分区
    从表中可以看到 auto_partitioning 策略已设置 "before": "3 days", "period": "8 hours",即自动分区管理操作会提前创建好此表未来 3 天的所有分区,每个分区的大小是 8 小时。例如,假如当前时间是 3 月 1 日 0:00,那么自动分区创建被触发,3 月 1 日 0:00 ~ 8:003 月 4 日 0:00 ~ 8:00 的所有分区将被创建好。到 3 月 1 日 8:01 时,自动分区管理操作会开始创建新一个 8 小时的分区,即 3 月 4 日 8:00 ~ 16:00 的分区。auto_partitioning 策略的以上设计,能够确保未来 3 天内的所有分区永远能提前被准备好。
    但是由于其他任务可能占用这个表,自动分区管理操作触发创建分区的 SQL 语句可能会跟其他任务产生锁竞争。为了减少锁竞争的次数,在 auto_partitioning_ex 里,增加了 batch_size 选项。使用以下语句设置:
    =# SELECT public.set_policy_action('metrics'::regclass, 'auto_create', '{ "batch_size": "4" }');

    这意味着一旦自动分区管理操作要开始创建一个分区,则会再多创建出 batch_size 个未来分区。因此当 3 月 1 日 0:01 将要创建 3 月 4 日 0:00 ~ 8:00 的分区时,它这次会一次性多创建 3 月 4 日 0:00 ~ 8:008:00 ~ 16:0016:00 ~ 24:003 月 5 日 0:00 ~ 8:00 这 4 个未来分区。 这样就避免了在 3月 1 日 ~ 3 月 4 日一段时间内再次触发分区创建,减少了锁竞争的次数。

  • batch_size 的默认值为 0,即不进行批量创建,此时 auto_partitioning_ex 行为跟 auto_partitioning 一致。
  • batch_size 的最大值为 100

注意!
批量创建的功能虽然可以使争锁的频率从每天一次,降低成为每几天一次,然而一旦自动分区管理操作执行具体的 SQL 命令,仍然不能从根本上避免与其他 SQL 命令发生锁竞争,这是数据库本身的工作原理决定的。
因此从数据库使用的角度,仍需要对查询和锁、自动分区管理操作的执行情况等设置必要的监控和告警。

  1. 强制保留特定历史分区
    从表中可以看到,我们创建了 apmtrap 分区。这意味着当你启用了 auto_partitioning/auto_partitioning_ex 的自动删除分区(retention)功能时,分区的名字里包含 apmtrap 字符串(如 “ssss_amptrap_sss”),的分区即使已经达到回收条件,也不会被删除,从而实现对这些历史分区的永久保留。
    此功能的设计目的是用于保持一个“陷阱”分区,用来接收那些常规时间范围外(如 1970-01-01)的数据。这个陷阱分区会替默认分区吞噬那些无用数据,从而抑制了默认分区的过快增长,同时陷阱分区又不会参与查询和 DDL 操作,保护表的查询性能。

  2. 自定义自动分区工作时段
    从上文 list_policy 结果中可以看到 "end_time": "00:00:00","start_time": "00:00:00"。实际上,start_time / end_time 参数被用以控制自动分区管理工作的时段,从而避免在业务繁忙时段创建分区。例如 start_time = 01:00:00,end_time = 04:00:00 代表在每天凌晨 1:00 ~ 4:00 允许自动分区管理工作。再如 start_time = 22:00:00,end_time = 03:00:00 代表在每晚 22:00 ~ 次日凌晨 3:00 期间允许自动分区管理工作。

  • 默认值为 start_time = 00:00:00,end_time = 00:00:00。时间戳相同代表不设限制,即全天 24 小时都允许工作。
    SELECT public.set_policy_action('table_name'::regclass, 'auto_create', '{ "start_time": "00:00:00", "end_time": "04:00:00" }');

注意!
设置的时间范围不应小于 1 小时,因为自动分区管理的检查周期默认是 1 小时,如果起止时间间隔不足 1 小时,则很有可能错过触发机会。 到达 end_time 截止时间,并不代表该时刻时分区处理会立即结束,已经触发的自动分区管理工作会继续直至完成。end_time 只能保证过了该时刻后,不会再有新一轮自动分区管理检查被触发,但是并不会强制结束已经触发的自动分区管理工作。因此在设定 end_time 参数时,你需要给即将到来的业务高峰留下足够的缓冲时间。

2 操作状态设置

从上文 list_policy 结果会注意到,包含一个叫做 disabled 的列。该列用来设置操作生效状态。目前无论哪种策略,retention 操作的状态都是disabled。

如要开启该操作,请调用 enable_policy_action

=# SELECT enable_policy_action('metrics'::regclass, 'retention');

如要禁用一种操作,请调用 disable_policy_action

=# SELECT disable_policy_action('metrics'::regclass, 'auto_split');

3 删除策略

drop_policy 为分区表删除策略:

=# SELECT drop_policy('metrics'::regclass);

注意!
每个分区表只能设置一种分区策略,所以删除策略只提供分区表名即可。

4 日志监控

4.1 apm_operation_log

apm_operation_log 记录了用户配置APM的每个操作履历。示例如下:

=# SELECT username = CURRENT_USER, relname, class_id, class_name, action, operation, mod_field, old_val, new_val
FROM matrixts_internal.apm_operation_log ORDER BY ts;
 ?column? |      relname       | class_id | class_name |   action   | operation | mod_field  |        old_val         |        new_val         
----------+--------------------+----------+------------+------------+-----------+------------+------------------------+------------------------
 t        | apm_test.split_set |          |            | test_split | add       | check_args |                        | {"age": "1 month"}
 t        | apm_test.split_set |          |            | test_split | add       | act_args   |                        | {"period": "2 weeks"}
 t        | apm_test.split_set |          |            | test_split | mod       | act_args   | {"period": "2 weeks"}  | {"period": "12 hours"}
 t        | apm_test.split_set |          |            | test_split | mod       | check_args | {"age": "1 month"}     | {"age": "15 days"}
 t        | apm_test.split_set |          |            | test_split | mod       | check_args | {"age": "15 days"}     | {"age": "5 days"}
 t        | apm_test.split_set |          |            | test_split | mod       | act_args   | {"period": "12 hours"} | {"period": "2 days"}
 t        | apm_test.split_set |          |            | test_split | drop      |            |                        | 
 t        | apm_test.split_set |          |            | test_split | add       | check_args |                        | {"age": "10 days"}
 t        | apm_test.split_set |          |            | test_split | add       | act_args   |                        | {"period": "1 hour"}
(9 rows)

4.2 apm_action_log

apm_action_log 记录APM后台每次自动发生分区操作的履历。示例如下:

=# (select * from matrixts_internal.apm_action_log order by ts limit 6) union all (select * from matrixts_internal.apm_action_log order by ts desc limit 6) order by 1;
             ts             |  pid  | reloid |         relname          |      nspname       | class_id |    class_name     |   action    |               check_func               |      check_args      |                    act_func
      |      act_args       |  state  |      message      | details | addopt |  client
----------------------------+-------+--------+--------------------------+--------------------+----------+-------------------+-------------+----------------------------------------+----------------------+------------------------------------------
------+---------------------+---------+-------------------+---------+--------+----------
 2022-11-18 15:48:45.963264 | 15776 |  18360 | mx_query_execute_history | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | START   | action start      |         |        | schedule
 2022-11-18 15:48:46.165418 | 15776 |  18360 | mx_query_execute_history | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | SUCCESS | do action success |         |        | schedule
 2022-11-18 15:48:46.172096 | 15776 |  18373 | mx_query_usage_history   | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | START   | action start      |         |        | schedule
 2022-11-18 15:48:46.332781 | 15776 |  18373 | mx_query_usage_history   | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | SUCCESS | do action success |         |        | schedule
 2022-11-19 00:48:45.992494 | 24930 |  18360 | mx_query_execute_history | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | START   | action start      |         |        | schedule
 2022-11-19 00:48:46.064604 | 24930 |  18360 | mx_query_execute_history | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | SUCCESS | do action success |         |        | schedule
 2022-11-21 00:48:47.708846 | 14091 |  18373 | mx_query_usage_history   | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | START   | action start      |         |        | schedule
 2022-11-21 00:48:47.812423 | 14091 |  18373 | mx_query_usage_history   | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | SUCCESS | do action success |         |        | schedule
 2022-11-22 00:48:46.552925 |  8539 |  18360 | mx_query_execute_history | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | START   | action start      |         |        | schedule
 2022-11-22 00:48:53.25854  |  8539 |  18360 | mx_query_execute_history | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | SUCCESS | do action success |         |        | schedule
 2022-11-22 00:48:53.261857 |  8539 |  18373 | mx_query_usage_history   | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | START   | action start      |         |        | schedule
 2022-11-22 00:48:55.625125 |  8539 |  18373 | mx_query_usage_history   | matrixmgr_internal |        2 | auto_partitioning | auto_create | matrixts_internal.apm_generic_incoming | {"before": "3 days"} | matrixts_internal.apm_generic_append_part
ition | {"period": "1 day"} | SUCCESS | do action success |         |        | schedule
(12 rows)