全部產品
Search
文件中心

:主鍵與唯一鍵(AUTO模式)

更新時間:Jul 06, 2024

本節介紹了在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不同的資料會被儲存到不同分區。

  1. 向list_tbl插入一條資料,執行成功後,該資料被儲存到p1分區。

    INSERT INTO list_tbl(order_id, city, name) VALUES (10001, "Beijing", "phone");
    Query OK, 1 row affected
  2. 向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' ")
  3. 向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唯一鍵不保證全域唯一,因此可能出現唯一鍵重複的情況。

  1. 沿用樣本11中的range_tbl,向表中插入一條資料,執行成功後,該資料被儲存到p1分區。

    INSERT INTO range_tbl(serial_id, order_time) VALUES (20001, '2022-01-01');
    Query OK, 1 row affected
  2. 向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' ")
  3. 向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產生唯一值作為主索引值,可保證主鍵全域唯一。