本节介绍了在AUTO模式数据库中,判断表的主键是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)
) SINGLE;
## 广播表
CREATE TABLE brd_tbl(
id bigint NOT NULL AUTO_INCREMENT,
name varchar(30),
PRIMARY KEY(id)
) BROADCAST;
分区表
在AUTO模式下,建表时如果没有指定分区键、分区算法,则称为自动分区表;建表时如果指定了分区键或者分区算法,称为手动分区表。
自动分区表
自动分区表中的主键都是Global主键,能保证全局唯一。
示例2:自动分区表中的Global主键
## 自动分区表
CREATE TABLE auto_tbl(
id bigint NOT NULL AUTO_INCREMENT,
name varchar(30),
PRIMARY KEY(id)
);
手动分区表
Global主键
在手动分区表中,如果主键列包含了全部分区列,该主键就是Global主键,能保证全局唯一。
示例3:手动分区表中的Global主键
表key_tbl的主键列是(id, name, addr)
,包含了所有分区列(id, addr)
,所以该表的主键是Global主键,能保证全局唯一。
CREATE TABLE key_tbl(
id bigint,
name varchar(10),
addr varchar(30),
PRIMARY KEY(id, name, addr)
) PARTITION BY KEY(id, addr);
Local主键
在手动分区表中,如果主键列未包含全部分区列,则该主键就是Local主键。
示例4:手动分区表中的Local主键
表list_tbl的分区列city未被包含在主键列中,所以该表的主键是Local主键,只能保证分区内唯一,无法保证全局唯一。
CREATE TABLE list_tbl(
order_id bigint,
city varchar(50),
name text,
PRIMARY KEY(order_id)
) PARTITION BY LIST(city)
(
PARTITION p1 VALUES IN ("Beijing"),
PARTITION p2 VALUES IN ("Shanghai"),
PARTITION p3 VALUES IN ("Guangzhou"),
PARTITION p4 VALUES IN ("Shenzhen"),
PARTITION p5 VALUES IN(DEFAULT)
);
示例5:Local主键无法保证全局唯一
由于Local主键只能保证分区内部唯一,不保证全局唯一,因此可能出现主键重复的情况。沿用示例4中的list_tbl表,该表使用city作为分区列,因此city不同的数据会被存储到不同分区。
向list_tbl插入一条数据,执行成功后,该数据被存储到p1分区。
INSERT INTO list_tbl(order_id, city, name) VALUES (10001, "Beijing", "phone"); Query OK, 1 row affected
向list_tbl表插入一条order_id相同且city相同的数据。由于city相同,数据仍将被存储到p1分区,执行SQL时发现插入失败,报主键冲突的错误。可以看到相同主键的值无法插入到相同分区,这说明Local主键可以保证在分区内唯一。
INSERT INTO list_tbl(order_id, city, name) VALUES (10001, "Beijing", "book"); (1062, "ERR-CODE: [TDDL-4614][ERR_EXECUTE_ON_MYSQL] Error occurs when execute on GROUP 'TEST_DB_P00000_GROUP' ATOM 'dskey_test_db_p00000_group#polardbx-storage-0-master#11.167.60.147-1766#test_db_p00000': Duplicate entry '10001' for key 'PRIMARY' ")
向list_tbl表插入一条order_id相同但city不同的数据,因为city的值为“Shanghai”,数据将被存储到p4分区,执行成功。此时list_tbl表内存在两行主键重复的数据,说明Local主键无法保证全局唯一。
INSERT INTO list_tbl (order_id, city, name) VALUES (10001, "Shenzhen", "camera"); Query OK, 1 row affected SELECT * FROM list_tbl; +----------+----------+--------+ | order_id | city | name | +----------+----------+--------+ | 10001 | Beijing | phone | | 10001 | Shenzhen | camera | +----------+----------+--------+ 2 rows in set
示例6:在含有重复主键的表上执行DDL,可能出现主键冲突报错
使用Local主键的表内可能存在重复的主键值,当在该表上执行数据重分布相关的操作时(如执行分区变更的DDL、将表同步至下游),可能会出现主键冲突的错误。
沿用示例5中的表list_tbl,该表已经存在两行主键相同的数据,一行存储在city为“Beijing”的分区,另一行存储在city为“Shenzhen”的分区。执行如下所示变更分区策略的DDL,使得city为“Beijing”和“Shenzhen”的数据存储在同一个分区,DDL会执行失败,报主键冲突的错误。这是因为该DDL使得主键重复的数据出现在同一个分区,违反了Local主键的分区内唯一性。
ALTER TABLE list_tbl
PARTITION BY LIST (city)
(
PARTITION p1 VALUES IN ("Beijing", "Shenzhen"),
PARTITION p2 VALUES IN ("Shanghai"),
PARTITION p3 VALUES IN ("Guangzhou"),
PARTITION p5 VALUES IN(DEFAULT)
);
(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 '10001' for key 'PRIMARY' ")
对于使用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唯一键,能保证全局唯一。
示例7:单表和广播表中的Global唯一键
## 单表
CREATE TABLE single_tbl(
serial_id bigint,
name varchar(30),
UNIQUE KEY(serial_id)
) SINGLE;
## 广播表
CREATE TABLE brd_tbl(
serial_id bigint,
name varchar(30),
UNIQUE KEY(serial_id)
) BROADCAST;
分区表
在AUTO模式下,建表时如果没有指定分区键、分区算法,则称为自动分区表;建表时如果指定了分区键或者分区算法,称为手动分区表。
自动分区表
自动分区表中的唯一键都是Global唯一键,能保证全局唯一。
示例8:自动分区表中的Global唯一键
## 自动分区表
CREATE TABLE auto_tbl(
serial_id bigint,
name varchar(30),
UNIQUE KEY(serial_id)
);
手动分区表
Global唯一键
在手动分区表中,如果唯一键列包含了全部分区列,该唯一键就是Global唯一键,能保证全局唯一。
示例9:手动分区表中的Global唯一键
表hash_tbl的唯一键列是(inner_id, type_id)
,包含了所有的分区列(type_id)
,所以该表的唯一键是Global唯一键。
CREATE TABLE hash_tbl(
type_id int,
inner_id int,
UNIQUE KEY(inner_id, type_id)
) PARTITION BY HASH(type_id);
示例10:手动分区表中由UNIQUE GLOBAL INDEX构成的全局唯一键
在手动分区表中,全局二级索引也是一个Global唯一键,能保证全局唯一。表key_tbl包含一个索引列为serial_id
的UNIQUE GLOBAL INDEX,它能保证serial_id
的全局唯一性,因此是Global唯一键。
CREATE TABLE key_tbl(
type_id int,
serial_id int,
UNIQUE GLOBAL INDEX u_sid(serial_id) PARTITION BY HASH(serial_id)
) PARTITION BY HASH(type_id);
Local唯一键
示例11:手动分区表中的Local唯一键
在手动分区表中,如果唯一键列未包含全部分区列,该唯一键就是Local唯一键。
表range_tbl的唯一键列是serial_id
,未包含分区列order_time
,所以该表的唯一键是Local唯一键,只能保证分区内唯一,无法保证全局唯一。
CREATE TABLE range_tbl(
id int primary key auto_increment,
serial_id int,
order_time datetime NOT NULL,
UNIQUE KEY(serial_id)
) PARTITION BY RANGE(order_time)
(
PARTITION p1 VALUES LESS THAN ('2022-12-31'),
PARTITION p2 VALUES LESS THAN ('2023-12-31'),
PARTITION p3 VALUES LESS THAN (MAXVALUE)
);
示例12:Local唯一键无法保证全局唯一
与Local主键类似,由于Local唯一键不保证全局唯一,因此可能出现唯一键重复的情况。
沿用示例11中的range_tbl,向表中插入一条数据,执行成功后,该数据被存储到p1分区。
INSERT INTO range_tbl(serial_id, order_time) VALUES (20001, '2022-01-01'); Query OK, 1 row affected
向range_tbl表插入一条
serial_id
相同,且order_time
为“2022-01-02”的数据。order_time
的值决定了数据仍将被存储到p1分区,执行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' ")
向range_tbl表插入一条
serial_id
相同,且order_time
为“2023-01-01”的数据。order_time
的值决定了数据将被存储到p3分区,执行成功。此时range_tbl表内存在两行唯一键重复的数据,这说明Local唯一键无法保证全局唯一。INSERT INTO range_tbl(serial_id, order_time) VALUES (20001, '2024-01-01'); Query OK, 1 row affected SELECT * FROM range_tbl; +----+-----------+---------------------+ | id | serial_id | order_time | +----+-----------+---------------------+ | 2 | 20001 | 2024-01-01 00:00:00 | | 1 | 20001 | 2022-01-01 00:00:00 | +----+-----------+---------------------+ 2 rows in set
示例13:在含有重复唯一键的表上执行DDL,可能出现唯一键冲突报错
与Local主键类似,用到Local唯一键的表内可能存在重复的唯一键值,当在该表上执行数据重分布相关的操作时(如执行分区变更的DDL、将表同步至下游),可能会出现唯一键冲突的错误。
沿用示例12中的表range_tbl,该表已经存在两行serial_id
相同的数据,一行存储在p1分区,另一行存储在p2分区。尝试执行变更表类型的DDL,使range_tbl表从手动分区表变成单表,这将引发range_tbl表内的数据重分布。
ALTER TABLE range_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 '200001' for key 'PRIMARY' ")
DDL执行失败,报了关于唯一键冲突的错误。这是因为DDL在把range_tbl转换为单表时,在单表内出现了重复的唯一键值,违反了单表内唯一键的唯一性。
对于使用Local唯一键的表,为避免唯一键值重复引发的唯一键冲突,应该由业务侧采取措施确保唯一键值的唯一性。
对于使用Local唯一键的表,如果已经存在唯一键值重复的情况,当往下游同步数据时出现唯一键冲突时,建议人工订正源端数据。
常见问题
Q:创建Global主键、Global唯一键有特殊的语法吗?
A:没有,使用与MySQL一样的语法创建主键、唯一键即可。请注意创建的主键、唯一键需要满足上文中关于Global主键、Global唯一键的定义。
Q:目前使用的主键是Local主键,但我想保证主键全局唯一,该怎么做?
A:请参见Sequence生成唯一值作为主键值,可保证主键全局唯一。