本文介绍了在AUTO模式数据库中使用Locality关键字指定数据库对象的存储位置的方法。本语法仅适用于AUTO模式数据库。

PolarDB-X支持在AUTO模式数据库中,通过Locality关键字来指定数据库、表或分区的存储位置,以实现数据隔离或数据的均匀分布。

当前Locality关键字实现的能力如下:

  • 允许定义数据库、表、分区级别的Locality属性,其他操作(缩容除外)均会在保证Locality约束的前提下改变数据分布。
  • 允许变更表组、分区组级别的Locality属性,修改后将自动触发相应的异步数据迁移任务。

前提条件

  • 实例内核版本需为5.4.14及以上。
  • 逻辑库的分区类型需为AUTO模式。

如何查看实例版本,请参见查看和升级实例版本

注意事项

  • 节点缩容后,包含被缩容节点的Locality定义将会自动失效。
  • 当前Locality属性通过DN节点的ID定义,备份恢复后原有Locality属性将会自动失效。

查看存储节点信息

假设已有一个PolarDB-X实例,您可以通过如下命令查看实例中的存储节点信息:

SHOW STORAGE;

返回结果如下:

+--------------------+----------------------------------+------------+-----------+----------+-------------+--------+-----------+-------+--------+
| STORAGE_INST_ID    | LEADER_NODE                      | IS_HEALTHY | INST_KIND | DB_COUNT | GROUP_COUNT | STATUS | DELETABLE | DELAY | ACTIVE |
+--------------------+----------------------------------+------------+-----------+----------+-------------+--------+-----------+-------+--------+
| polardbx-ng28-dn-0 | polardbx-ng28-dn-0-cands-0:14289 | true       | MASTER    | 1        | 2           | 0      | false     | null  | null   |
| polardbx-ng28-dn-1 | polardbx-ng28-dn-1-cands-0:14176 | true       | MASTER    | 1        | 1           | 0      | true      | null  | null   |
| polardbx-ng28-dn-2 | polardbx-ng28-dn-2-cands-0:14568 | true       | MASTER    | 1        | 1           | 0      | true      | null  | null   |
| polardbx-ng28-dn-3 | polardbx-ng28-dn-3-cands-0:16796 | true       | MASTER    | 1        | 1           | 0      | true      | null  | null   |
| polardbx-ng28-gms  | polardbx-ng28-dn-0-cands-0:14289 | true       | META_DB   | 2        | 2           | 0      | false     | null  | null   |
+--------------------+----------------------------------+------------+-----------+----------+-------------+--------+-----------+-------+--------+
说明
  • STORAGE_INST_ID列为存储节点名,即当前在Locality中使用的名字。
  • INST_KIND列为存储节点类型,值为META_DB的节点为元数据节点,不能用于存储用户数据。
  • STATUS列为存储节点状态,值为0的节点为可用节点。
  • DELTETABLE列表明该节点是否可被缩容,值为false的节点不可被缩容,其中包含元数据节点和一个指定存储节点(下面简称为0号存储节点)。

创建数据库时指定存储位置

在创建数据库时指定存储位置以实现数据隔离。

  1. 在实例中创建一个AUTO模式数据库,并通过如下命令指定其存储位置,详细语法请参见CREATE DATABASE
    CREATE DATABASE db1 LOCALITY='dn=polardbx-ng28-dn-0,polardbx-ng28-dn-1,polardbx-ng28-dn-2' MODE = 'auto';
  2. 创建成功后,您可以通过如下语句查看数据库的存储位置信息。
    SHOW CREATE DATABASE `db1`;

    返回结果如下:

    +----------+--------------------------------------------------------------------------------------------------------------------+
    | DATABASE | CREATE DATABASE                                                                                                    |
    +----------+--------------------------------------------------------------------------------------------------------------------+
    | db1      | CREATE DATABASE `db1` /* MODE = 'auto' LOCALITY = "dn=polardbx-ng28-dn-0,polardbx-ng28-dn-1,polardbx-ng28-dn-2" */ |
    +----------+--------------------------------------------------------------------------------------------------------------------+
    1 row in set (0.04 sec)
  3. 您还可以通过如下命令查看创建在该数据库下的逻辑分库和物理分库信息。
    SHOW DS;

    返回结果如下:

    +----+--------------------+--------------------+---------------------------------+----------------------+---------+
    | ID | STORAGE_INST_ID    | DB                 | GROUP                           | PHY_DB               | MOVABLE |
    +----+--------------------+--------------------+---------------------------------+----------------------+---------+
    |  0 | polardbx-ng28-dn-0 | db1                | DB1_P00000_GROUP                | db1_p00000           |       1 |
    |  1 | polardbx-ng28-dn-1 | db1                | DB1_P00001_GROUP                | db1_p00001           |       1 |
    |  2 | polardbx-ng28-dn-2 | db1                | DB1_P00002_GROUP                | db1_p00002           |       1 |
    |  3 | polardbx-ng28-gms  | information_schema | INFORMATION_SCHEMA_SINGLE_GROUP | polardbx_info_schema |       0 |
    +----+--------------------+--------------------+---------------------------------+----------------------+---------+
    4 rows in set (0.04 sec)
    说明 数据库的存储位置必须包含0号节点,表和分区的存储位置不受此限制。

创建逻辑表时指定存储位置

在创建逻辑表时指定存储位置以实现数据隔离,目前支持在单表和Range、Hash、List三种分区方式的分区表中使用Locality关键字。

  1. 在实例中创建一个逻辑表,并指定其存储位置。详细语法参见CREATE TABLE(AUTO模式)
    CREATE TABLE t_order (
      `id` bigint(11) NOT NULL AUTO_INCREMENT BY GROUP,
      `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 locality = 'dn=polardbx-ng28-dn-1,polardbx-ng28-dn-2';
  2. 执行以下语句,查看表的定义。
    SHOW CREATE TABLE `t_order`;

    返回信息如下:

    +---------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | TABLE   | CREATE TABLE                                                                                                                                                                                                                                                                                                                                                                                        |
    +---------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | 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,
        `order_detail` longtext,
        PRIMARY KEY (`id`),
        INDEX `l_i_order` (`order_id`)
    ) ENGINE = InnoDB DEFAULT CHARSET = utf8
    /* LOCALITY='dn=polardbx-ng28-dn-1,polardbx-ng28-dn-2' */ |
    +---------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    1 row in set (0.05 sec)
  3. 执行以下语句,查看逻辑表的各个分区的数据分布情况。
    SHOW TOPOLOGY `t_order`;

    返回信息如下:

    +----+------------------+--------------------+----------------+-------------+--------------------+
    | ID | GROUP_NAME       | TABLE_NAME         | PARTITION_NAME | PHY_DB_NAME | DN_ID              |
    +----+------------------+--------------------+----------------+-------------+--------------------+
    |  0 | DB1_P00002_GROUP | t_order_18dV_00001 | p2             | db1_p00002  | polardbx-ng28-dn-2 |
    |  1 | DB1_P00001_GROUP | t_order_18dV_00000 | p1             | db1_p00001  | polardbx-ng28-dn-1 |
    |  2 | DB1_P00001_GROUP | t_order_18dV_00002 | p3             | db1_p00001  | polardbx-ng28-dn-1 |
    +----+------------------+--------------------+----------------+-------------+--------------------+
    3 rows in set (0.15 sec)
    说明
    • 创建表时Locality关键字指定的存储节点集合,必须是数据库对应存储节点集合的子集。
    • 如果所创建的表为单表,则只允许Locality关键字声明单个DN节点作为存储节点。
    • 当逻辑表与表组匹配时,表与表组、分区和分区组的Locality属性必须完全相同才能匹配成功。
    • 逻辑表、逻辑表的表组、逻辑表的GSI、逻辑表GSI的表组默认具备相同的Locality属性。

创建逻辑表的分区时指定存储位置

在创建逻辑表时,通过指定分区级别的存储节点,可以将同一张逻辑表的不同分区分布在不同DN上。

  1. 在实例中创建一个逻辑表,并指定其分区的Locality。
    CREATE TABLE orders_region(
     order_id int AUTO_INCREMENT primary key,
     customer_id int,
     country varchar(64),
     city varchar(64),
     order_time datetime not null)
    PARTITION BY LIST COLUMNS(country,city)
    (
      PARTITION p1 VALUES IN (('China','Shanghai')) LOCALITY = 'dn=polardbx-ng28-dn-2',
      PARTITION p2 VALUES IN (('China','Beijing')) LOCALITY = 'dn=polardbx-ng28-dn-2',
      PARTITION p3 VALUES IN (('China','Hangzhou')) ,
      PARTITION p4 VALUES IN (('China','Nanjing')) ,
      PARTITION p5 VALUES IN (('China','Guangzhou')) ,
      PARTITION p6 VALUES IN (('China','Shenzhen')) ,
      PARTITION p7 VALUES IN (('China','Wuhan')) ,
      PARTITION p8 VALUES IN (('America','New York'))
    ) LOCALITY = 'dn=polardbx-ng28-dn-0,polardbx-ng28-dn-1';
                
  2. 执行以下语句,查看表的定义。
    SHOW CREATE TABLE `orders_region`;

    返回信息如下:

    +---------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | TABLE         | CREATE TABLE                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
    +---------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | orders_region | CREATE TABLE `orders_region` (
        `order_id` int(11) NOT NULL AUTO_INCREMENT,
        `customer_id` int(11) DEFAULT NULL,
        `country` varchar(64) DEFAULT NULL,
        `city` varchar(64) DEFAULT NULL,
        `order_time` datetime NOT NULL,
        PRIMARY KEY (`order_id`),
        KEY `auto_shard_key_country_city` USING BTREE (`country`, `city`)
    ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4
    /* LOCALITY='dn=polardbx-ng28-dn-0,polardbx-ng28-dn-1' */
    PARTITION BY LIST COLUMNS(`country`,`city`)
    (PARTITION `p1` VALUES IN (('China','Shanghai')) ENGINE = InnoDB LOCALITY='dn=polardbx-ng28-dn-2',
     PARTITION `p2` VALUES IN (('China','Beijing')) ENGINE = InnoDB LOCALITY='dn=polardbx-ng28-dn-2',
     PARTITION `p3` VALUES IN (('China','Hangzhou')) ENGINE = InnoDB,
     PARTITION `p4` VALUES IN (('China','Nanjing')) ENGINE = InnoDB,
     PARTITION `p5` VALUES IN (('China','Guangzhou')) ENGINE = InnoDB,
     PARTITION `p6` VALUES IN (('China','Shenzhen')) ENGINE = InnoDB,
     PARTITION `p7` VALUES IN (('China','Wuhan')) ENGINE = InnoDB,
     PARTITION `p8` VALUES IN (('America','New York')) ENGINE = InnoDB) |
    +---------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    1 row in set (0.05 sec)
  3. 执行以下语句,查看逻辑表的各个分区的数据分布情况。
    SHOW TOPOLOGY `orders_region`;

    返回信息如下:

    +----+------------------+--------------------------+----------------+-------------+--------------------+
    | ID | GROUP_NAME       | TABLE_NAME               | PARTITION_NAME | PHY_DB_NAME | DN_ID              |
    +----+------------------+--------------------------+----------------+-------------+--------------------+
    |  0 | DB1_P00002_GROUP | orders_region_RlsY_00000 | p1             | db1_p00002  | polardbx-ng28-dn-2 |
    |  1 | DB1_P00002_GROUP | orders_region_RlsY_00001 | p2             | db1_p00002  | polardbx-ng28-dn-2 |
    |  2 | DB1_P00001_GROUP | orders_region_RlsY_00003 | p4             | db1_p00001  | polardbx-ng28-dn-1 |
    |  3 | DB1_P00001_GROUP | orders_region_RlsY_00004 | p5             | db1_p00001  | polardbx-ng28-dn-1 |
    |  4 | DB1_P00001_GROUP | orders_region_RlsY_00006 | p7             | db1_p00001  | polardbx-ng28-dn-1 |
    |  5 | DB1_P00000_GROUP | orders_region_RlsY_00002 | p3             | db1_p00000  | polardbx-ng28-dn-0 |
    |  6 | DB1_P00000_GROUP | orders_region_RlsY_00005 | p6             | db1_p00000  | polardbx-ng28-dn-0 |
    |  7 | DB1_P00000_GROUP | orders_region_RlsY_00007 | p8             | db1_p00000  | polardbx-ng28-dn-0 |
    +----+------------------+--------------------------+----------------+-------------+--------------------+
    8 rows in set (0.25 sec)
    说明 分区的存储节点必须是数据库存储节点的子集,但可以不是逻辑表的存储节点的子集。

修改表组的存储节点

动态变更表组的存储节点。

语法
ALTER TABLEGROUP identifier SET LOCALITY = locality_option
locality_option:
    'dn=storage_inst_id_list'
 |  ''
示例

以前文中的orders_region表为例,该表位于表组`tg3`中,现对其存储节点进行变更:

ALTER TABLEGROUP `tg3` SET LOCALITY = `dn=polardbx-ng28-dn-0`;

变更操作后,表的拓扑如下:

SHOW TOPOLOGY `orders_region`;
+----+------------------+--------------------------+----------------+-------------+--------------------+
| ID | GROUP_NAME       | TABLE_NAME               | PARTITION_NAME | PHY_DB_NAME | DN_ID              |
+----+------------------+--------------------------+----------------+-------------+--------------------+
|  0 | DB1_P00002_GROUP | orders_region_RlsY_00000 | p1             | db1_p00002  | polardbx-ng28-dn-2 |
|  1 | DB1_P00002_GROUP | orders_region_RlsY_00001 | p2             | db1_p00002  | polardbx-ng28-dn-2 |
|  2 | DB1_P00000_GROUP | orders_region_RlsY_00002 | p3             | db1_p00000  | polardbx-ng28-dn-0 |
|  3 | DB1_P00000_GROUP | orders_region_RlsY_00003 | p4             | db1_p00000  | polardbx-ng28-dn-0 |
|  4 | DB1_P00000_GROUP | orders_region_RlsY_00004 | p5             | db1_p00000  | polardbx-ng28-dn-0 |
|  5 | DB1_P00000_GROUP | orders_region_RlsY_00005 | p6             | db1_p00000  | polardbx-ng28-dn-0 |
|  6 | DB1_P00000_GROUP | orders_region_RlsY_00006 | p7             | db1_p00000  | polardbx-ng28-dn-0 |
|  7 | DB1_P00000_GROUP | orders_region_RlsY_00007 | p8             | db1_p00000  | polardbx-ng28-dn-0 |
+----+------------------+--------------------------+----------------+-------------+--------------------+
8 rows in set (0.17 sec)
            

在同一表组内,定义过Locality的分区的数据存储位置,将遵从其Locality约束,其他分区将根据表组的Locality约束进行数据迁移。修改表组或分区组的存储位置将会在元信息变更后快速返回,后台生成相应表组的分区迁移任务异步执行

可通过information_schema.ddl_plan查询对应的分区迁移任务REBALANCE TABLEGROUP tg3的执行进度。

SELECT * FROM information_schema.ddl_plan WHERE table_schema = "db1";
+----+---------------------+---------------------+--------------+------------------------------------------------------------------+---------+-----------+----------+-------------+--------+--------+---------------------+---------------------+----------------+
| ID | plan_id             | job_id              | table_schema | ddl_stmt                                                         | state   | ddl_type  | progress | retry_count | result | extras | gmt_created         | gmt_modified        | resource       |
+----+---------------------+---------------------+--------------+------------------------------------------------------------------+---------+-----------+----------+-------------+--------+--------+---------------------+---------------------+----------------+
|  1 | 1465819565798723584 | 1465819579241467904 | db1          | REBALANCE TABLEGROUP `tg3`  EXPLAIN=false ASYNC=true DEBUG=false | SUCCESS | REBALANCE |      100 |           0 |        |        | 2022-05-24 14:37:58 | 2022-05-24 14:38:11 | tablegroup:tg3 |
+----+---------------------+---------------------+--------------+------------------------------------------------------------------+---------+-----------+----------+-------------+--------+--------+---------------------+---------------------+----------------+

修改分区组的存储节点

动态变更分区组的存储节点。

语法
ALTER TABLEGROUP identifier SET PARTITIONS part_name LOCALITY = locality_option
locality_option:
    'dn=storage_inst_id_list'
 |  ''
示例

以前文中的orders_region表为例,该表位于表组`tg3`中,现对其分区p3的Locality进行变更:

ALTER TABLEGROUP `tg3` SET PARTITIONS p3 LOCALITY = `dn=polardbx-ng28-dn-1`;

变更后表的拓扑如下:

SHOW TOPOLOGY orders_region;
+----+------------------+--------------------------+----------------+-------------+--------------------+
| ID | GROUP_NAME       | TABLE_NAME               | PARTITION_NAME | PHY_DB_NAME | DN_ID              |
+----+------------------+--------------------------+----------------+-------------+--------------------+
|  0 | DB1_P00002_GROUP | orders_region_RlsY_00000 | p1             | db1_p00002  | polardbx-ng28-dn-2 |
|  1 | DB1_P00002_GROUP | orders_region_RlsY_00001 | p2             | db1_p00002  | polardbx-ng28-dn-2 |
|  2 | DB1_P00001_GROUP | orders_region_RlsY_00002 | p3             | db1_p00001  | polardbx-ng28-dn-1 |
|  3 | DB1_P00000_GROUP | orders_region_RlsY_00003 | p4             | db1_p00000  | polardbx-ng28-dn-0 |
|  4 | DB1_P00000_GROUP | orders_region_RlsY_00004 | p5             | db1_p00000  | polardbx-ng28-dn-0 |
|  5 | DB1_P00000_GROUP | orders_region_RlsY_00005 | p6             | db1_p00000  | polardbx-ng28-dn-0 |
|  6 | DB1_P00000_GROUP | orders_region_RlsY_00006 | p7             | db1_p00000  | polardbx-ng28-dn-0 |
|  7 | DB1_P00000_GROUP | orders_region_RlsY_00007 | p8             | db1_p00000  | polardbx-ng28-dn-0 |
+----+------------------+--------------------------+----------------+-------------+--------------------+
8 rows in set (0.11 sec)

分区变更操作中的Locality属性传播

当前AUTO模式下的分区变更操作包括表组级分区变更语法(AUTO模式)变更表类型及分区策略(AUTO模式)

在分区变更操作中,新的分区组和表组一般自动继承原有的Locality属性,并且变更后的表组和分区组也将自动满足数据分布约束,但是以下三种情形例外:

  • 对于表的类型变更操作,当目标类型为单表或者广播表时,表的Locality属性默认为空。
  • 对于明确涉及数据热点的分区变更操作,新分区Locality默认为空。
  • 对于merge partition操作,当原分区集合成员的Locality不同时,新分区Locality默认为空。

分区变更操作涉及的Locality变更说明:

类型(简称) 变更对象 变更对象的Locality属性是否不变 备注
分区表拆分变更 -
表的类型变更 仅在单表变更为分区表时不变。 分区表变更为单表和广播表时Locality属性会自动失效。
分区迁移(move partition) 分区组 -
分区合并(merge partition) 分区组 新分区仅在所有旧分区的locality属性相同时继承旧分区的locality。 无法继承时,新分区Locality默认为空值。
分区分裂(split partition) 分区组 -
分区按热点分裂(split by hot value) 分区组 新分区Locality默认为空值。
分区热点值提取(extract) 分区组 新分区Locality默认为空值。
分区删除(drop partition) 分区组 - -
分区新增(add partition) 分区组 -
LIST分区值修改(modify partition) 分区组 -
分区重命名(rename partition) 分区组 -

典型使用场景

  • 创建数据库时,可通过Localiy对逻辑库级别的数据进行隔离,数据库中的表和分区会自动分布在指定的DN上。
  • 创建单表时,可通过Locality指定单表的存储节点,从而避免单表数量过多对0号节点的存储压力。
  • 创建分区表时,可通过指定分区级别的Locality,将同一张逻辑表的不同分区控制在不同的DN上,例如结合LIST分区可对同一张逻辑表中不同地域数据进行隔离。
  • 当数据分区产生热点数据时,可通过分区变更和Locality为热点分区指定单独的DN,从而隔离其物理存储资源。