22.2.6 Subpartitioning

子分区(也称为复合分区)是分区 table 中每个分区的进一步划分。考虑以下CREATE TABLE语句:

CREATE TABLE ts (id INT, purchased DATE)
    PARTITION BY RANGE( YEAR(purchased) )
    SUBPARTITION BY HASH( TO_DAYS(purchased) )
    SUBPARTITIONS 2 (
        PARTITION p0 VALUES LESS THAN (1990),
        PARTITION p1 VALUES LESS THAN (2000),
        PARTITION p2 VALUES LESS THAN MAXVALUE
    );

tablets具有 3 个RANGE分区。每个分区p0p1p2进一步分为 2 个子分区。实际上,整个 table 分为3 * 2 = 6个分区。但是,由于PARTITION BY RANGE子句的作用,其中的前 2 个仅在purchased列中存储值小于 1990 的那些记录。

在 MySQL 5.7 中,可以对被RANGELIST分区的 table 进行子分区。子分区可以使用HASHKEY分区。这也称为复合分区。

Note

SUBPARTITION BY HASHSUBPARTITION BY KEY通常分别遵循与PARTITION BY HASHPARTITION BY KEY相同的语法规则。exceptions 是SUBPARTITION BY KEY(与PARTITION BY KEY不同)当前不支持默认列,因此即使 table 具有显式主键,也必须指定用于此目的的列。这是我们正在努力解决的已知问题;有关更多信息和示例,请参见子分区问题

也可以使用SUBPARTITION子句显式定义子分区,以指定各个子分区的选项。例如,如上例所示,创建相同 tablets的更为冗长的方式是:

CREATE TABLE ts (id INT, purchased DATE)
    PARTITION BY RANGE( YEAR(purchased) )
    SUBPARTITION BY HASH( TO_DAYS(purchased) ) (
        PARTITION p0 VALUES LESS THAN (1990) (
            SUBPARTITION s0,
            SUBPARTITION s1
        ),
        PARTITION p1 VALUES LESS THAN (2000) (
            SUBPARTITION s2,
            SUBPARTITION s3
        ),
        PARTITION p2 VALUES LESS THAN MAXVALUE (
            SUBPARTITION s4,
            SUBPARTITION s5
        )
    );

这里列出了一些语法上的注意事项:

  • 每个分区必须具有相同数量的子分区。

  • 如果在分区 table 的任何分区上使用SUBPARTITION显式定义任何子分区,则必须全部定义它们。换句话说,以下语句将失败:

CREATE TABLE ts (id INT, purchased DATE)
    PARTITION BY RANGE( YEAR(purchased) )
    SUBPARTITION BY HASH( TO_DAYS(purchased) ) (
        PARTITION p0 VALUES LESS THAN (1990) (
            SUBPARTITION s0,
            SUBPARTITION s1
        ),
        PARTITION p1 VALUES LESS THAN (2000),
        PARTITION p2 VALUES LESS THAN MAXVALUE (
            SUBPARTITION s2,
            SUBPARTITION s3
        )
    );

即使包含SUBPARTITIONS 2子句,该语句仍将失败。

  • 每个SUBPARTITION子句必须(至少)包括该子分区的名称。否则,您可以为子分区设置任何所需的选项,或允许其采用该选项的默认设置。

  • 子分区名称在整个 table 中必须唯一。例如,以下CREATE TABLE语句在 MySQL 5.7 中有效:

CREATE TABLE ts (id INT, purchased DATE)
    PARTITION BY RANGE( YEAR(purchased) )
    SUBPARTITION BY HASH( TO_DAYS(purchased) ) (
        PARTITION p0 VALUES LESS THAN (1990) (
            SUBPARTITION s0,
            SUBPARTITION s1
        ),
        PARTITION p1 VALUES LESS THAN (2000) (
            SUBPARTITION s2,
            SUBPARTITION s3
        ),
        PARTITION p2 VALUES LESS THAN MAXVALUE (
            SUBPARTITION s4,
            SUBPARTITION s5
        )
    );

子分区可以与特别大的MyISAMtable 一起使用,以在许多磁盘上分配数据和索引。假设您安装了 6 个磁盘,分别为/disk0/disk1/disk2等。现在考虑以下示例:

CREATE TABLE ts (id INT, purchased DATE)
    ENGINE = MYISAM
    PARTITION BY RANGE( YEAR(purchased) )
    SUBPARTITION BY HASH( TO_DAYS(purchased) ) (
        PARTITION p0 VALUES LESS THAN (1990) (
            SUBPARTITION s0
                DATA DIRECTORY = '/disk0/data'
                INDEX DIRECTORY = '/disk0/idx',
            SUBPARTITION s1
                DATA DIRECTORY = '/disk1/data'
                INDEX DIRECTORY = '/disk1/idx'
        ),
        PARTITION p1 VALUES LESS THAN (2000) (
            SUBPARTITION s2
                DATA DIRECTORY = '/disk2/data'
                INDEX DIRECTORY = '/disk2/idx',
            SUBPARTITION s3
                DATA DIRECTORY = '/disk3/data'
                INDEX DIRECTORY = '/disk3/idx'
        ),
        PARTITION p2 VALUES LESS THAN MAXVALUE (
            SUBPARTITION s4
                DATA DIRECTORY = '/disk4/data'
                INDEX DIRECTORY = '/disk4/idx',
            SUBPARTITION s5
                DATA DIRECTORY = '/disk5/data'
                INDEX DIRECTORY = '/disk5/idx'
        )
    );

在这种情况下,将单独的磁盘用于数据和每个RANGE的索引。许多其他的变化是可能的。另一个示例可能是:

CREATE TABLE ts (id INT, purchased DATE)
    ENGINE = MYISAM
    PARTITION BY RANGE(YEAR(purchased))
    SUBPARTITION BY HASH( TO_DAYS(purchased) ) (
        PARTITION p0 VALUES LESS THAN (1990) (
            SUBPARTITION s0a
                DATA DIRECTORY = '/disk0'
                INDEX DIRECTORY = '/disk1',
            SUBPARTITION s0b
                DATA DIRECTORY = '/disk2'
                INDEX DIRECTORY = '/disk3'
        ),
        PARTITION p1 VALUES LESS THAN (2000) (
            SUBPARTITION s1a
                DATA DIRECTORY = '/disk4/data'
                INDEX DIRECTORY = '/disk4/idx',
            SUBPARTITION s1b
                DATA DIRECTORY = '/disk5/data'
                INDEX DIRECTORY = '/disk5/idx'
        ),
        PARTITION p2 VALUES LESS THAN MAXVALUE (
            SUBPARTITION s2a,
            SUBPARTITION s2b
        )
    );

在这里,存储如下:

  • 具有purchased日期的 1990 年之前的行会占用大量空间,因此被分成 4 种方式,其中一个单独的磁盘专用于数据和组成分区p0的两个子分区(s0as0b)的索引。换一种说法:

  • 子分区s0a的数据存储在/disk0上。

    • 子分区s0a的索引存储在/disk1上。

    • 子分区s0b的数据存储在/disk2上。

    • 子分区s0b的索引存储在/disk3上。

  • 包含 1990 年到 1999 年(分区p1)之间日期的行所需要的空间没有 1990 年之前的空间大。它们被分成 2 个磁盘(/disk4/disk5),而不是像p0中存储的旧记录那样划分为 4 个磁盘:

  • 属于p1的第一个子分区(s1a)的数据和索引存储在/disk4上-/disk4/data中的数据和/disk4/idx中的索引。

    • 属于p1的第二子分区(s1b)的数据和索引存储在/disk5上-/disk5/data中的数据和/disk5/idx中的索引。
  • 反映从 2000 年到现在(分区p2)的日期的行所占用的空间不如前两个范围中的任何一个所需。当前,将所有这些存储在默认位置就足够了。

将来,当从 2000 年开始的十年中的购买数量增加到默认位置不再提供足够空间的程度时,可以使用ALTER TABLE ... REORGANIZE PARTITION语句移动相应的行。有关如何执行此操作的说明,请参见第 22.3 节“分区 Management”

NO_DIR_IN_CREATE服务器 SQL 模式生效时,分区定义中不允许DATA DIRECTORYINDEX DIRECTORY选项。在 MySQL 5.7 中,定义子分区时也不允许使用这些选项(错误#42954)。