本節介紹了在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產生唯一值作為主索引值,可保證主鍵全域唯一。