变更表类型及分区策略(AUTO模式)

当单表数据量过大导致查询缓慢,或初期设定的分区策略不合理引发数据热点时,原有的表结构可能成为业务瓶颈。为解决此类问题,PolarDB分布式版提供了在线(Online)变更表类型和分区策略的功能。可以在不中断业务(不锁表、不阻塞DML)的情况下,对表进行数据重分布,实现单表、广播表、分区表之间的相互转换,或调整现有分区表的分区键、分区算法,以优化数据布局,提升数据库整体性能和可扩展性。

重要
  • 此操作属于重型DDL任务,其本质是在线进行全量数据迁移和重分布。

  • 执行期间会消耗大量的CPU、IO和网络资源,可能对实例性能产生影响,且耗时与数据量成正比。

  • 建议在业务低峰期执行,并配合使用任务管理命令进行监控。

适用范围

执行变更前,需确保环境满足以下条件。

  • 数据库模式:仅适用于AUTO模式数据库

  • 实例版本:仅支持PolarDB-X 2.0,且内核小版本需为5.4.13及以上版本。

  • 全局二级索引(GSI):仅支持PolarDB-X 2.0,且内核小版本需为5.4.14及以上版本对带有GSI的分区表进行分区变更。变更过程中,GSI的数据会同步进行重分布。

说明

工作原理

  • 在线DDL:所有变更操作均以在线方式执行,期间不锁定原表,DML/SELECT操作均可正常进行,保障业务连续性。

  • 数据重分布:变更表类型或分区策略的核心是数据的物理迁移。例如:

    • 将单表转为分区表时,数据会从单个数据节点(DN)分散到多个DN。

    • 变更分区键时,数据会根据新的分区策略在DN间重新平衡。此过程是资源消耗的主要原因。

  • 原子性与可回滚:整个DDL任务具备原子性。如果任务因唯一键冲突、磁盘空间不足等原因执行失败,系统会确保原表数据完好无损,业务不受影响。失败的任务可通过 CANCEL DDL命令回滚该任务。

变更表类型

PolarDB分布式版支持单表、广播表和分区表三种类型,可根据业务场景的变化在它们之间进行在线转换。详细信息,请参见CREATE TABLE(AUTO模式)

说明

变更表类型时,如果原表为主键分区表(即分区键与主键一致),在将其变更为普通表后,该表将不再具备主键分区表的特性,例如自动分区策略或索引转换规则。详细信息,请参见AUTO模式下的主键拆分

将单表或广播表变更为分区表

此场景适用于单表数据量持续增长,已成为或即将成为性能瓶颈,需要将其水平拆分以提升扩展性和查询性能。

语法说明

使用ALTER TABLE语句并指定新的分区策略。关于partition_options的详细语法,请参见分区表

ALTER TABLE table_name PARTITION BY partition_options;

示例

  1. 创建一个示例表t_order

    CREATE TABLE t_order (
      `id` bigint(11) NOT NULL AUTO_INCREMENT,
      `order_id` varchar(20) DEFAULT NULL,
      `buyer_id` varchar(20) DEFAULT NULL,
      `seller_id` varchar(20) DEFAULT NULL,
      `order_snapshot` longtext DEFAULT NULL,
      `order_detail` longtext DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `l_i_order` (`order_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  2. 因业务发展导致单表t_order数据量剧增,现决定将其变更为以order_id为分区键的分区表。

    -- 将单表 t_order 变更为以 order_id 为分区键的KEY分区表
    ALTER TABLE t_order PARTITION BY KEY(`order_id`);
    
    -- 也可以在变更的同时指定分区数量
    ALTER TABLE t_order PARTITION BY KEY(`order_id`) PARTITIONS 8;

将分区表或单表变更为广播表

该场景适用于数据量较小且几乎不增长的表,但这些表频繁与其他大型表进行关联查询(JOIN),例如配置表和字典表。通过将表数据复制到每个数据节点(DN),可以消除跨库JOIN所带来的网络开销,从而提升关联查询的性能。

语法说明

使用 ALTER TABLE语句并指定BROADCAST子句。

ALTER TABLE table_name BROADCAST;
说明

广播表的数据会在所有DN节点上存有副本,会占用更多存储空间。

示例

  1. 创建一个示例表t_order_1

    CREATE TABLE t_order_1 (
      `id` bigint(11) NOT NULL AUTO_INCREMENT,
      `order_id` varchar(20) DEFAULT NULL,
      `buyer_id` varchar(20) DEFAULT NULL,
      `seller_id` varchar(20) DEFAULT NULL,
      `order_snapshot` longtext DEFAULT NULL,
      `order_detail` longtext DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `l_i_order` (`order_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  2. t_order_1表变更为广播表。

    ALTER TABLE t_order_1 BROADCAST;

将分区表或广播表变更为单表

此场景适用于数据量较小且无需进行水平扩展的表。将表的数据集中到单个数据节点(DN)上,以简化表结构。

语法说明

使用ALTER TABLE语句并指定SINGLE子句。

ALTER TABLE table_name SINGLE;
说明

将分区表变更为单表时,所有分片的数据都会汇聚到同一个DN节点。需确保目标节点的磁盘空间充足,避免因空间不足导致操作失败。

示例

  1. 创建一个示例表t_order_2

    CREATE TABLE t_order_2 (
      `id` bigint(11) NOT NULL AUTO_INCREMENT,
      `order_id` varchar(20) DEFAULT NULL,
      `buyer_id` varchar(20) DEFAULT NULL,
      `seller_id` varchar(20) DEFAULT NULL,
      `order_snapshot` longtext DEFAULT NULL,
      `order_detail` longtext DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `l_i_order` (`order_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    PARTITION BY KEY(`order_id`)
    PARTITIONS 8;
  2. t_order_2表变更为单表。

    ALTER TABLE t_order_2 SINGLE;

变更分区表的分区策略

当现有的分区策略无法满足业务需求时,例如由于分区键选择不当导致的数据倾斜,或业务查询模式的变化,可以在线调整分区策略。通过对分区键、分区函数或分区数量进行修改,重新优化数据的物理分布,以解决数据热点问题,适应新的业务模型。

语法说明

使用ALTER TABLE语句并指定新的分区策略。

ALTER TABLE tbl_name PARTITION BY new_partition_options;
说明
  • 如果原表是主键分区表(即分区键与主键一致),在变更分区策略后,若新的分区键不再是主键,该表将不再具备主键分区表的特性(如自动创建分区等)。详细信息,请参见AUTO模式下的主键拆分

  • 在变更分区策略后,如果主键列未包含全部分区列,则该主键为Local主键,只能保证分区内唯一,无法保证全局唯一。详细信息,请参见主键与唯一键(AUTO模式)

示例

变更分区键

创建一个t_order_3表当前按order_id分区,但多数查询基于buyer_id,导致跨分片查询较多。现需要将分区键变更为buyer_id,并将分区数设定为16。

  1. 原表结构。

    CREATE TABLE t_order_3 (
      `id` bigint(11) NOT NULL AUTO_INCREMENT,
      `order_id` varchar(20) DEFAULT NULL,
      `buyer_id` varchar(20) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 
    PARTITION BY KEY(`order_id`)
    PARTITIONS 8;
  2. 执行变更。

    ALTER TABLE t_order_3 PARTITION BY KEY(buyer_id) PARTITIONS 16;
  3. 变更后表结构:系统会自动创建新的分区键索引auto_shard_key_buyer_id

    SHOW FULL CREATE TABLE  t_order_3;
    +-----------+-----------------------------------------------------------------------+
    | Table     | Create Table                                                          |
    +-----------+-----------------------------------------------------------------------+
    | t_order_3 | CREATE TABLE `t_order_3` (
            `id` bigint NOT NULL AUTO_INCREMENT,
            `order_id` varchar(20) DEFAULT NULL,
            `buyer_id` varchar(20) DEFAULT NULL,
            PRIMARY KEY (`id`),
            LOCAL KEY `auto_shard_key_buyer_id` USING BTREE (`buyer_id`)
    ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 DEFAULT COLLATE = utf8mb4_0900_ai_ci
    PARTITION BY KEY(`buyer_id`)
    PARTITIONS 16
    /* tablegroup = `tg12` */ |
    +-----------+---------------------------------------------------------------------+

变更分区策略

创建一个t_order_4表目前根据id进行RANGE分区,但大多数查询是基于order_idbuyer_id。因此,需要将其变更为以order_idbuyer_id作为分区键的KEY分区,并将分区数设定为16。

  1. 原表结构。

    CREATE TABLE t_order_4 (
      `id` bigint(11) NOT NULL AUTO_INCREMENT,
      `order_id` varchar(20) DEFAULT NULL,
      `buyer_id` varchar(20) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 
    PARTITION BY RANGE(`id`)
    (
      PARTITION p1 VALUES LESS THAN (100),
      PARTITION p2 VALUES LESS THAN (1000),
      PARTITION P3 VALUES LESS THAN MAXVALUE
    );
  2. 执行变更。

    ALTER TABLE t_order_4 PARTITION BY KEY(order_id, buyer_id) PARTITIONS 16;
  3. 变更后表结构:系统会自动创建新的分区键索引auto_shard_key_order_id_buyer_id

    SHOW FULL CREATE TABLE  t_order_4;
    +-----------+--------------------------------------------------------------------------------+
    | Table     | Create Table                                                                   |
    +-----------+--------------------------------------------------------------------------------+
    | t_order_4 | CREATE TABLE `t_order_4` (
            `id` bigint NOT NULL AUTO_INCREMENT,
            `order_id` varchar(20) DEFAULT NULL,
            `buyer_id` varchar(20) DEFAULT NULL,
            PRIMARY KEY (`id`),
            LOCAL KEY `auto_shard_key_order_id_buyer_id` USING BTREE (`order_id`, `buyer_id`)
    ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb3
    PARTITION BY KEY(`order_id`,`buyer_id`)
    PARTITIONS 16
    /* tablegroup = `tg15` */ |
    +-----------+------------------------------------------------------------------------------+

增加分区数量

随着数据量增长,需要增加分区数以分散数据。

  1. 原表结构。

    CREATE TABLE t_order_5 (
      `id` bigint(11) NOT NULL AUTO_INCREMENT,
      `order_id` varchar(20) DEFAULT NULL,
      `buyer_id` varchar(20) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 
    PARTITION BY KEY(`order_id`)
    PARTITIONS 8;
  2. 执行变更。

    ALTER TABLE t_order_5 PARTITION BY KEY(order_id) PARTITIONS 128;
  3. 变更后,分区数将从8个扩展到128个。

    SHOW FULL CREATE TABLE  t_order_5;
    +-----------+----------------------------------------------------------+
    | Table     | Create Table                                             |
    +-----------+----------------------------------------------------------+
    | t_order_4 | CREATE TABLE `t_order_5` (
            `id` bigint NOT NULL AUTO_INCREMENT,
            `order_id` varchar(20) DEFAULT NULL,
            `buyer_id` varchar(20) DEFAULT NULL,
            PRIMARY KEY (`id`),
            LOCAL KEY `auto_shard_key_order_id` USING BTREE (`order_id`)
    ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb3
    PARTITION BY KEY(`order_id`)
    PARTITIONS 128
    /* tablegroup = `tg13` */ |
    +-----------+---------------------------------------------------------+

任务管理与应急处理

对于耗时较长的数据重分布DDL任务,可使用以下命令进行全生命周期管理。

  • 监控任务进度:使用SHOW DDL命令可以查看当前正在执行或历史的DDL任务列表及其状态。

  • 暂停与恢复任务:如果DDL任务对业务高峰期的性能产生较大影响,可以先暂停,待低峰期再恢复。

    -- 暂停任务,从 SHOW DDL 的结果中获取 JobId
    PAUSE DDL <JobId>;
    
    -- 恢复任务
    CONTINUE DDL <JobId>;
  • 终止与回滚任务:如果DDL任务执行失败,或希望主动取消正在执行的任务,可使用CANCEL DDL命令。该操作会回滚所有变更,使表结构和数据恢复到任务开始前的状态。

    -- 回滚任务,从 SHOW DDL 的结果中获取 JobId
    CANCEL DDL <JobId>;

应用于生产环境

  • 执行时机:建议在业务低峰期执行此类操作,以减少对线上业务的潜在性能影响。

  • 容量规划:执行变更前,需评估目标数据节点(DN)的磁盘空间、CPUIOPS资源是否充足,特别是对于分区表转单表或数据重分布剧烈的场景,避免因资源不足导致任务失败。

  • 操作前备份:尽管DDL任务失败可回滚,但在生产环境执行任何重大变更前,建议完成一次全量数据备份。

  • 验证:任务完成后,可使用SHOW CREATE TABLE <tablename>查看新的表定义,或使用SHOW TOPOLOGY查看新的数据分片拓扑。

常见问题

为什么分区键变更的DDL任务会执行失败?如何处理?

常见失败原因包括实例崩溃、目标节点磁盘空间不足、或数据迁移过程中出现唯一索引冲突。任务失败不会损坏原表数据,也不会阻塞正常的DML和查询。可通过SHOW DDL查看失败原因,处理完问题后(如清理磁盘、处理冲突数据),使用CANCEL DDL命令回滚失败的任务,然后重新尝试执行。

变更分区策略这类操作耗时都很长吗?新增一个空分区这样的操作也慢吗?

并非所有分区操作都是重型操作。变更表类型和变更分区策略因为涉及全量数据迁移,所以耗时较长。而为RANGE/LIST分区表增加一个未来的空分区(例如ALTER TABLE ... ADD PARTITION),通常是秒级的元数据操作,对业务影响极小。

如果新分区键上已经有一个索引,系统还会自动创建 auto_shard_key_... 索引吗?

系统会检查新分区键列是否是某个现有索引的最左前缀。如果是,则会复用该索引进行分区剪枝,不会创建新索引。如果不是,为保证查询性能,系统会自动创建一个新的本地索引。

如何查看数据库是AUTO模式还是DRDS模式?

执行SHOW CREATE DATABASE <database_name>,返回结果中MODE属性即为数据库模式。