普通表转换为分区表

当单张普通表的数据量超过10亿行或1 TB时,建议采用分区表。本文将介绍如何使用ALTER语法将单张普通表转换为分区表。

前提条件

在将普通表转换为分区表时,若普通表存在主键(Primary Key)和唯一键(Unique Key),则主键和唯一键均需包含分区键(Partition Key)字段。以确保每个分区内数据及全局数据的唯一性。详细示例,请参见示例

例如:

  • 若按单列order_id进行分区,则主键和唯一键应包含order_id(如PRIMARY KEY(id, order_id))。

  • 若按多列region_idorder_date进行分区,则主键和唯一键应包含region_idorder_date(如PRIMARY KEY(id, region_id, order_date))。

注意事项

  • 普通表转换为分区表的过程中,所有的数据都需要重新进行读写,属于COPY DDL操作。因此,转换时间较长,且整个转换过程会阻塞当前表上的所有DML操作。

    说明

    COPY DDL执行算法是需要将表中所有的数据复制到新表中。在数据复制期间,会持有原表的SNW(SHARED_NO_WRITE)锁。因此,在执行DDL操作期间仅支持读操作,不允许执行并发写入操作,对业务影响较大。更多信息,请参见DDL执行算法

  • 如果需要按照时间做RANGE分区,并且需要将所有数据放在一个历史分区中,请参见普通表快速转换为RANGE分区表

支持转换的分区表类型

所有的分区表类型(如HASHRANGE以及LIST等)均支持普通表转换分区表操作。详细分区表类型请参见分区表类型及使用说明INTERVAL RANGE分区

语法

ALTER TABLE table_name
PARTITION BY RANGE {(expr) | COLUMNS(column_list)}
(partition_definition [, partition_definition] ...);

其中,partition_definition的定义与各个类型的分区表中的partition_definition保持一致,支持二级分区。

说明

PolarDB MySQL支持的分区表类型及使用,请参见分区表类型及使用说明

示例

单列分区

此处以普通表test_users为例,为您演示如何将普通表转换为分区表。

  1. 创建普通表test_users,并插入数据。

    CREATE TABLE test_users (
        id INT AUTO_INCREMENT,
        region_id INT NOT NULL, -- 分区键
        email VARCHAR(100) NOT NULL,
        name VARCHAR(50),
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id, region_id),  -- 主键必须包含分区键字段
        UNIQUE KEY (email, region_id) -- 唯一键必须包含分区键字段
    );
    INSERT INTO test_users (region_id, email, name) VALUES
    (1, 'alice@example.com', 'Alice'),
    (2, 'bob@example.com', 'Bob'),
    (3, 'charlie@example.com', 'Charlie');
  2. 将普通表test_users转换为分区表。

    • 转换为LIST分区表。更多信息,请参见LIST

      ALTER TABLE test_users 
          PARTITION BY LIST (region_id) (
              PARTITION p_east VALUES IN (1, 2),  -- region_id12的数据到p_east
              PARTITION p_west VALUES IN (3)      -- region_id3的数据到p_west
          );
    • 转换为HASH分区表。更多信息,请参见HASH

      ALTER TABLE test_users 
          PARTITION BY HASH(region_id)
          PARTITIONS 4;  -- 分为4个哈希分区
    • 转换为RANGE分区表。更多信息,请参见RANGE

      ALTER TABLE test_users 
          PARTITION BY RANGE (region_id) (
              PARTITION p0 VALUES LESS THAN (2),  -- region_id < 2
              PARTITION p1 VALUES LESS THAN (3),  -- 2 ≤ region_id < 3
              PARTITION p2 VALUES LESS THAN MAXVALUE
          );

多列分区

此处以普通表orders为例,为您演示如何将普通表转换为分区表。

  1. 创建普通表orders,并插入数据。

    CREATE TABLE orders (
        order_id INT AUTO_INCREMENT,
        region_id INT NOT NULL,    -- 分区键字段1
        order_date DATE NOT NULL,  -- 分区键字段2
        customer_id INT,
        amount DECIMAL(10,2),
        PRIMARY KEY (order_id, region_id, order_date),             -- 主键必须包含所有分区键字段
        UNIQUE KEY (customer_id, order_id, region_id, order_date)  -- 唯一键必须包含所有分区键字段
    );
    INSERT INTO orders (region_id, order_date, customer_id, amount) VALUES
    (1, '2022-12-31', 1001, 99.99),
    (1, '2023-05-01', 1002, 199.99),
    (2, '2023-06-01', 1003, 299.99);
  2. 将普通表orders转换为RANGE分区表。更多信息,请参见RANGE

    ALTER TABLE orders
        PARTITION BY RANGE COLUMNS(region_id, order_date) (
            PARTITION p0 VALUES LESS THAN (1, '2023-01-01'),  -- region_id < 1 或 region_id=1 且 order_date < '2023-01-01'
            PARTITION p1 VALUES LESS THAN (2, '2024-01-01'),  -- region_id < 2 或 region_id=2 且 order_date < '2024-01-01'
            PARTITION p2 VALUES LESS THAN (MAXVALUE, MAXVALUE)
        );

常见问题

普通表转分区表时,报错:A PRIMARY KEY/UNIQUE INDEX must include all columns in the table's partitioning function?

您可以通过以下两种方式解决:

  • 根据前提条件,修改普通表的主键和唯一键,使其均需包含分区键字段。例如:

    • 若按单列order_id进行分区,则主键和唯一键应包含order_id(如PRIMARY KEY(id, order_id))。

    • 若按多列region_idorder_date进行分区,则主键和唯一键应包含region_idorder_date(如PRIMARY KEY(id, region_id, order_date))。

  • 使用UNIQUE CHECK IGNORE(UCI)功能,支持分区表的主键和唯一键不包含分区键字段,您可以选择任意列作为分区键。

相关文档

专家面对面

如需了解更多关于普通表转换为分区表的内容,可通过钉钉搜索群号入群咨询。您可以直接@群内专家,并附上您要咨询的问题。

钉钉群号:24490017825。