本节介绍了在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张分表。
向order_tbl插入一条数据,执行成功。
INSERT INTO order_tbl(order_id, city, name) VALUES (10001, "Beijing", "phone"); Query OK, 1 row affected
向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' ")
向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个分表。
向info_tbl插入一条数据,执行成功。
INSERT INTO info_tbl(serial_id, order_time) VALUES (20001, '2022-01-01'); Query OK, 1 row affected
向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' ")
向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生成唯一值作为主键值,可保证主键全局唯一。