文档

主键与唯一键(DRDS模式)

更新时间:

本节介绍了在DRDS模式数据库中,判断表的主键是Global主键还是Local主键,表的唯一键是Global唯一键还是Local唯一键的方法。

主键

PolarDB-X中,主键分为Global主键与Local主键。区别如下:

  • 能保证全局唯一,就称为Global主键;

  • 只保证分表内唯一,则称为Local主键。

单表和广播表

单表和广播表中的主键都是Global主键,能保证全局唯一。

示例1:单表和广播表中的Global主键

## 单表
CREATE TABLE single_tbl(
 id bigint NOT NULL AUTO_INCREMENT, 
 name varchar(30), 
 PRIMARY KEY(id)
);

## 广播表
CREATE TABLE brd_tbl(
 id bigint NOT NULL AUTO_INCREMENT, 
 name varchar(30), 
 PRIMARY KEY(id)
) BROADCAST;

分库分表

Global主键

在分库分表中,如果主键列包含了所有拆分键,该主键就是Global主键,能保证全局唯一。

示例2:分库分表中的Global主键

表user_tbl中,分库的拆分键为id,分表的拆分键为name,主键列(id, name, addr)包含了所有拆分键,所以该表的主键是Global主键,能保证全局唯一。

CREATE TABLE user_tbl(
 id bigint,
 name varchar(10),
 addr varchar(30),
 PRIMARY KEY(id, name, addr)
) DBPARTITION BY HASH(id) TBPARTITION BY HASH(name) TBPARTITIONS 4;

Local主键

在分库分表中,如果主键列未包含全部拆分键,该主键就是Local主键。

示例3:分库分表中的Local主键

表order_tbl的主键列是order_id,主键列未包含分库的拆分键city,因此该表的主键是Local主键,只能保证分表内唯一,无法保证全局唯一。

CREATE TABLE order_tbl(
 order_id bigint,
 city varchar(50),
 name text,
 PRIMARY KEY(order_id)
) DBPARTITION BY HASH(city);

示例4:Local主键无法保证全局唯一

由于Local主键只能保证分表内部唯一,不保证全局唯一,因此可能出现主键重复的情况。沿用示例3中的order_tbl表,该表使用city作为分库拆分键,且只分库不分表,因此每个分库都只有1张分表。

  1. 向order_tbl插入一条数据,执行成功。

    INSERT INTO order_tbl(order_id, city, name) VALUES (10001, "Beijing", "phone");
    Query OK, 1 row affected
  2. 向order_tbl表插入一条order_id相同且city相同的数据。由于city相同,数据仍将被存储到和第一次插入的数据相同的分表。执行SQL发现插入失败,报主键冲突的错误。可以看到相同主键的值无法插入到相同分表,这说明Local主键可以保证在分表内部唯一。

    INSERT INTO order_tbl(order_id, city, name) VALUES (10001, "Beijing", "book");
    (1062, "ERR-CODE: [TDDL-4614][ERR_EXECUTE_ON_MYSQL] Error occurs when execute on GROUP 'D25_000002_GROUP' ATOM 'dskey_d25_000002_group#polardbx-storage-0-master#11.167.60.147-1766#d25_000002': Duplicate entry '10001' for key 'PRIMARY' ")
  3. 向order_tbl表插入一条order_id相同但city不同的数据,因为city的值为“Shenzhen”,由于这次city的值不同于第一次插入的数据,这次的数据将被插入到不同的分表。

    执行SQL,发现执行成功。此时order_tbl表内存在两行主键重复的数据,这说明Local主键无法保证全局唯一。

    INSERT INTO order_tbl (order_id, city, name) VALUES (10001, "Shenzhen", "camera");
    Query OK, 1 row affected
    
    SELECT * FROM order_tbl;
    +----------+----------+--------+
    | order_id | city | name |
    +----------+----------+--------+
    | 10001 | Beijing | phone |
    | 10001 | Shenzhen | camera |
    +----------+----------+--------+
    2 rows in set

示例5:在含有重复主键的表上执行DDL,可能出现主键冲突报错

使用Local主键的表内可能存在重复的主键值,当在该表上执行数据重分布相关的操作时(如执行变更表类型的DDL、将表同步至下游),可能会出现主键冲突的错误。

表order_tbl已经存在两行主键相同的数据,它们分别存储在不同的分表内。执行变更表类型的DDL,通过把分库分表变成单表,使得city为“Beijing”和“Shenzhen”的数据存储在一起。

ALTER TABLE order_tbl SINGLE;
(4700, '[17399c0a2fc00000][30.221.117.14:8527][d25]ERR-CODE: [TDDL-4700][ERR_SERVER] server error by The DDL job has been rollback. Please check the ddl stmt. jobId: 1673540305653071872 ')

DDL执行失败。这是因为在将分库分表转为单表时,主键重复的数据同时出现在新的单表中,违反了单表中的主键唯一性。

对于使用Local主键的表,为避免主键值重复引发的主键冲突,建议:

  • 使用auto_increment属性,由PolarDB-X生成主键值;

  • 同时,避免业务侧手动写入指定的主键值。

    重要

    对于使用Local主键的表,如果已经存在主键重复的情况,往下游同步数据的时候需避免下游出现主键冲突。例如将含Local主键的表通过DTS向云原生数据仓库AnalyticDB MySQL版进行同步时,如果云原生数据仓库AnalyticDB MySQL版的主键沿用PolarDB-X的主键,就可能出现冲突,此时建议将云原生数据仓库AnalyticDB MySQL版的主键设为PolarDB-X端表的主键列和所有拆分键的集合。

唯一键

与主键类似,在PolarDB-X中,唯一键分为Global唯一键与Local唯一键。当创建出的唯一键:

  • 能保证全局唯一,就称为Global唯一键;

  • 只保证分表内唯一,则称为Local唯一键。

本节将介绍在不同场景下,如何判断表的唯一键是Global唯一键还是Local唯一键。

单表和广播表

单表和广播表中的唯一键都是Global唯一键,能保证全局唯一。

示例6:单表和广播表中的Global唯一键

## 单表
CREATE TABLE single_tbl(
 serial_id bigint,
 name varchar(30), 
 UNIQUE KEY(serial_id)
);

## 广播表
CREATE TABLE brd_tbl(
 serial_id bigint,
 name varchar(30), 
 	UNIQUE KEY(serial_id)
) BROADCAST;

分库分表

Global唯一键

在分库分表中,如果唯一键列包含了全部拆分键,该唯一键就是Global唯一键,能保证全局唯一。

示例7:分库分表中的Global唯一键

表type_tbl的唯一键列是(inner_id, type_id),包含了所有的拆分键type_id,所以该表的唯一键是Global唯一键。

CREATE TABLE type_tbl(
 type_id int,
 inner_id int,
 UNIQUE KEY(inner_id, type_id)
) DBPARTITION BY HASH(type_id);

在分库分表中,全局二级索引也是一个Global唯一键,能保证全局唯一。

示例8:分库分表表中由UNIQUE GLOBAL INDEX构成的全局唯一键

表type_tbl2包含一个索引列为serial_id的UNIQUE GLOBAL INDEX,它能保证serial_id的全局唯一性,因此是Global唯一键。

CREATE TABLE type_tbl2(
 type_id int,
 serial_id int,
 UNIQUE GLOBAL INDEX u_sid(serial_id) DBPARTITION BY HASH(serial_id)
) DBPARTITION BY HASH(type_id);

Local唯一键

在分库分表中,如果唯一键列未包含全部拆分键,该唯一键就是Local唯一键。

示例9:分库分表中的Local唯一键

表info_tbl的唯一键列是serial_id,未包含拆分键order_time,所以该表的唯一键是Local唯一键,只能保证分表内唯一,无法保证全局唯一。

CREATE TABLE info_tbl(
 id int primary key auto_increment,
 serial_id int,
 	order_time date NOT NULL,
 UNIQUE KEY(serial_id)
) DBPARTITION BY YYYYMM(order_time);

示例10:Local唯一键无法保证全局唯一

与Local主键类似,由于Local唯一键不保证全局唯一,因此可能出现唯一键重复的情况。

沿用示例9中的info_tbl表,该表使用order_time作为分库拆分键,且只分库不分表,因此每个分库中只有1个分表。

  1. 向info_tbl插入一条数据,执行成功。

    INSERT INTO info_tbl(serial_id, order_time) VALUES (20001, '2022-01-01');
    Query OK, 1 row affected
  2. 向info_tbl表插入一条serial_id相同,且order_time为“2022-01-02”的数据。order_time的年月值与第一次插入的相同,因此数据将被存储到与第一次插入的数据一样的分表,执行SQL发现插入失败,报唯一键冲突的错误。相同唯一键的值无法插入到相同的分表,这说明Local唯一键可以保证在分表内部唯一。

    INSERT INTO info_tbl(serial_id, order_time) VALUES (20001, '2022-01-02');
    (1062, "ERR-CODE: [TDDL-4614][ERR_EXECUTE_ON_MYSQL] Error occurs when execute on GROUP 'D25_000001_GROUP' ATOM 'dskey_d25_000001_group#polardbx-storage-1-master#11.167.60.147-1766#d25_000001': Duplicate entry '20001' for key 'serial_id' ")
  3. 向info_tbl表插入一条serial_id相同,且order_time为“2023-03-01”的数据。order_time的值决定了数据将被存储到和第一次插入数据不同的分表,执行成功。此时serial_id表中存在两行唯一键重复的数据,这说明Local唯一键无法保证全局唯一。

    INSERT INTO info_tbl(serial_id, order_time) VALUES (20001, '2023-03-01');
    Query OK, 1 row affected
    
    SELECT * FROM info_tbl;
    +--------+-----------+------------+
    | id | serial_id | order_time |
    +--------+-----------+------------+
    | 100006 | 20001 | 2023-03-01 |
    | 100001 | 20001 | 2022-01-01 |
    +--------+-----------+------------+

示例11:在含有重复唯一键的表上执行DDL,可能会出现唯一键冲突报错

与Local主键类似,用到Local唯一键的表内可能存在重复的唯一键值,当在该表上执行数据重分布相关的操作时(如执行变更表类型的DDL、将表同步至下游),可能会出现唯一键冲突的错误。

表info_tbl已经存在两行serial_id相同的数据,它们分别存储在不同的分表内。尝试执行变更表类型的DDL,使info_tbl表从分库分表变成单表,这将引发info_tbl表内的数据重分布。

ALTER TABLE info_tbl SINGLE;
(4700, "ERR-CODE: [TDDL-4700][ERR_SERVER] server error by Failed to execute the DDL task. Caused by: ERR-CODE: [TDDL-5321][ERR_GLOBAL_SECONDARY_INDEX_BACKFILL_DUPLICATE_ENTRY] Duplicated entry '20001' for key 'PRIMARY' ")

DDL执行失败,报了关于唯一键冲突的错误。这是因为DDL在把info_tbl转换为单表时,在单表内出现了重复的唯一键值,违反了单表内唯一键的唯一性。

对于使用Local唯一键的表,为避免唯一键值重复引发的唯一键冲突,应该由业务侧采取措施确保唯一键值的唯一性。

重要

对于使用Local唯一键的表,如果已经存在唯一键值重复的情况,当往下游同步数据时出现唯一键冲突时,建议人工订正源端数据。

常见问题

  • Q:创建Global主键、Global唯一键有特殊的语法吗?

    A:没有,使用与MySQL一样的语法创建主键、唯一键即可。请注意创建的主键、唯一键需要满足上文中关于Global主键、Global唯一键的定义。

  • Q:目前使用的主键是Local主键,但我想保证主键全局唯一,该怎么做?

    A:请参见Sequence生成唯一值作为主键值,可保证主键全局唯一。

  • 本页导读 (0)
文档反馈