本頁面討論如何使用預設值運算式,在資料表中產生主鍵值。本頁面的資訊適用於 GoogleSQL 方言資料庫和 PostgreSQL 方言資料庫。這些策略有以下優點:
- 避免資源使用率不均。
- 簡化從其他資料庫的遷移作業。
- 將主要邏輯封裝在資料庫中,這樣就不必擔心在應用程式中管理邏輯。
- 在大多數情況下,您不需要自行建立及管理序列。
自動產生主鍵的方法
如要自動產生主鍵值,您可以在含有 DEFAULT 運算式的資料欄中使用下列策略:
- 產生 UUID 第 4 版值的 UUID 函式。
IDENTITY欄,可自動為索引鍵和非索引鍵欄產生整數值。- PostgreSQL 中的
SERIAL和 GoogleSQL 中的AUTO_INCREMENT,是IDENTITY資料欄的資料定義語言 (DDL) 別名。 - 具有
bit_reversed_positive選項的結構定義物件SEQUENCE。SEQUENCE適用於 GoogleSQL 和 PostgreSQL。 - 如果資料表沒有使用者定義的主鍵,系統會自動產生
rowid欄。
通用唯一識別碼 (UUID)
Spanner 可以自動產生 UUID 第 4 版,做為主鍵。UUID 適用於新應用程式和含有大量資料列的資料表。 這些索引鍵大致平均分布在索引鍵空間中,可避免大規模的熱點。UUID 生成功能可建立大量值 (2122),且每個值實際上都是不重複的。舉例來說,如要達到 50% 的碰撞機率,您需要 2.71×1018 個值,或每秒 10 億個值,持續 86 年。這個範圍很廣,因此在大型資料表中使用 UUID 時,可確保值不重複。無論是在資料庫或用戶端產生 UUID,都是不重複的。建議您盡可能使用 UUID。如果用戶端產生的 UUID 是以小寫形式序列化,且符合 RFC 4122 規定,您可以在同一個資料表中安全地混合使用用戶端和 Spanner 產生的 UUID。
如要為需要預設值的資料欄產生預設值,可以使用 GoogleSQL 函式 NEW_UUID 或 PostgreSQL 函式 gen_random_uuid()。以下範例說明如何建立資料表,其中 FanId 鍵資料欄在值資料欄中具有產生的 UUID 做為預設值。
GoogleSQL
CREATE TABLE Fans (
FanId UUID DEFAULT (NEW_UUID()),
Name STRING(MAX),
) PRIMARY KEY (FanId);
PostgreSQL
CREATE TABLE Fans (
FanId uuid DEFAULT gen_random_uuid(),
Name text,
PRIMARY KEY (FanId)
);
GoogleSQL
INSERT INTO Fans (Name) VALUES ('Melissa Garcia')
THEN RETURN FanId;
PostgreSQL
INSERT INTO fans (name) VALUES ('Melissa Garcia')
RETURNING fanid;
這項陳述式會傳回類似下列內容的結果:
| FanId |
|---|
| 6af91072-f009-4c15-8c42-ebe38ae83751 |
如要進一步瞭解產生的 UUID 函式,請參閱 GoogleSQL 或 PostgreSQL 參考頁面。
IDENTITY 個資料欄
使用 IDENTITY 欄,即可自動為索引鍵和非索引鍵欄產生整數值。IDENTITY 欄不需要您手動維護基礎序列,也不需要管理欄與基礎序列之間的關係。當您捨棄自動產生的身分欄時,Spanner 也會自動刪除基礎序列。
如要使用 IDENTITY 資料欄,您可以在產生序列時提供起始整數值,也可以讓 Spanner 為您產生整數序列。如要提供起始整數值,請使用 START
COUNTER WITH 選項,並使用正數 INT64 做為起始值。
Spanner 會使用這個值,為自動產生的內部序列計數器設定下一個值,並在將值插入這個資料欄之前,先反轉位元。
在 Spanner 中,GoogleSQL 和 PostgreSQL 都支援 IDENTITY 欄。
GoogleSQL
以下範例說明如何使用 IDENTITY 資料欄,在透過 CREATE TABLE 指令建立新資料表時,為 SingerId 建立自動產生的整數主鍵資料欄:
CREATE TABLE Singers (
SingerId INT64 GENERATED BY DEFAULT AS IDENTITY (BIT_REVERSED_POSITIVE),
Name STRING(MAX),
Rank INT64
) PRIMARY KEY (SingerId);
您也可以使用 START COUNTER WITH 選項,指定資料欄的計數器起始值。在下列範例中,系統會為 SingerId 建立自動產生的整數資料欄,其中包含位元反轉的正值,以及從 1,000 開始的內部計數器。
CREATE TABLE Singers (
SingerId INT64 GENERATED BY DEFAULT AS IDENTITY (BIT_REVERSED_POSITIVE START COUNTER WITH 1000),
Name STRING(MAX),
Rank INT64
) PRIMARY KEY (SingerId);
PostgreSQL
以下範例說明如何使用 IDENTITY 資料欄,在透過 CREATE
TABLE 指令建立新資料表時,為 SingerId 建立自動產生的整數資料欄:
CREATE TABLE Singers (
SingerId bigint GENERATED BY DEFAULT AS IDENTITY (BIT_REVERSED_POSITIVE),
Name text,
PRIMARY KEY (SingerId)
);
您也可以使用 START
COUNTER WITH 選項,指定資料欄的計數器起始值。在下列範例中,系統會為 SingerId 建立自動產生的整數資料欄,產生位元反轉的正值和內部計數器,而位元反轉前的內部計數器會從 1,000 開始。
CREATE TABLE Singers (
SingerId bigint GENERATED BY DEFAULT AS IDENTITY (BIT_REVERSED_POSITIVE START COUNTER WITH 1000),
Name text,
PRIMARY KEY (SingerId)
);
SERIAL 和 AUTO_INCREMENT
Spanner 在 PostgreSQL 中支援 SERIAL,在 GoogleSQL 中支援 AUTO_INCREMENT,這些都是 IDENTITY 資料欄的 DDL 別名,用於建立不重複的整數資料欄。您必須先設定資料庫 default_sequence_kind 選項,才能使用 SERIAL 或 AUTO_INCREMENT。您可以使用下列 SQL 陳述式設定資料庫 default_sequence_kind 選項:
GoogleSQL
ALTER DATABASE db SET OPTIONS (default_sequence_kind = 'bit_reversed_positive');
CREATE TABLE Singers (
id INT64 AUTO_INCREMENT PRIMARY KEY,
name STRING(MAX),
);
PostgreSQL
ALTER DATABASE db SET spanner.default_sequence_kind = 'bit_reversed_positive';
CREATE TABLE Singers (
id serial PRIMARY KEY,
name text
);
由於 SERIAL 和 AUTO_INCREMENT 會對應至 IDENTITY 欄,因此序列化結構定義時不會看到這兩個欄。針對這個結構定義,GetDatabaseDDL 的輸出內容會是:
GoogleSQL
ALTER DATABASE db SET OPTIONS (default_sequence_kind = 'bit_reversed_positive');
CREATE TABLE Singers (
id INT64 GENERATED BY DEFAULT AS IDENTITY,
name STRING(MAX)
) PRIMARY KEY (id);
PostgreSQL
ALTER DATABASE db SET spanner.default_sequence_kind = 'bit_reversed_positive';
CREATE TABLE Singers (
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
name character varying,
PRIMARY KEY(id)
);
位元反轉序列
位元反轉序列是產生整數序列並反轉位元的結構定義物件。這個物件會對私有內部 Spanner 計數器執行位元反轉,確保唯一性。產生的位元反轉值有助於避免在主鍵中使用時,大規模出現熱點。
在 Spanner 中,您可以使用 SEQUENCE DDL 陳述式和 bit_reversed_positive 屬性,建立、變更或捨棄產生位元反轉正值的序列 (GoogleSQL 或
PostgreSQL)。
每個序列都會維護一組內部計數器,並使用這些計數器產生值。序列計數器會將輸入提供給位元反轉演算法。
使用 GoogleSQL GET_NEXT_SEQUENCE_VALUE 或 PostgreSQL nextval 函式做為預設值,以 DEFAULT 運算式定義資料欄時,Spanner 會自動呼叫函式,並將位元反轉的輸出值放入資料欄。位元反轉序列特別適合用於主鍵,因為位元反轉值會平均分布在鍵空間中,因此不會造成熱點。
以下範例說明如何建立位元反轉序列和資料表,其中索引鍵資料欄會將序列做為預設值:
GoogleSQL
CREATE SEQUENCE SingerIdSequence OPTIONS (
sequence_kind="bit_reversed_positive"
);
CREATE TABLE Singers (
SingerId INT64 DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE SingerIdSequence)),
Name STRING(MAX),
Rank INT64
) PRIMARY KEY (SingerId);
PostgreSQL
CREATE SEQUENCE SingerIdSequence bit_reversed_positive;
CREATE TABLE Singers (
SingerId bigint DEFAULT nextval('SingerIdSequence'),
Name text,
PRIMARY KEY (SingerId)
);
然後您可以使用下列 SQL 陳述式插入並傳回主鍵值:
GoogleSQL
INSERT INTO Singers (Name) VALUES ('Example Singer')
THEN RETURN SingerId;
PostgreSQL
INSERT INTO Singers (name) VALUES ('Example Singer')
RETURNING SingerId;
這項陳述式會傳回類似下列內容的結果:
| SingerId |
|---|
| 3458764513820540928 |
建立資料表,但不定義主鍵
如果您在建立資料表時未提供任何主鍵,Spanner 會建立名為 rowid 的隱藏資料欄。這個資料欄會做為主鍵。根據預設,rowid 欄是使用 INT64 值的IDENTITY欄,並由位元反轉序列支援,可自動產生金鑰。
您可以透過下列方式建立資料表,而不定義主鍵:
GoogleSQL
CREATE TABLE Singers (
Name STRING(MAX),
Rank INT64
);
PostgreSQL
CREATE TABLE Singers (
Name text,
Rank bigint
);
產生的結構定義如下所示:
GoogleSQL
CREATE TABLE Singers (
Name STRING(MAX),
Rank INT64,
rowid INT64 NOT NULL GENERATED BY DEFAULT AS IDENTITY
(BIT_REVERSED_POSITIVE) HIDDEN
) PRIMARY KEY (rowid);
PostgreSQL
CREATE TABLE Singers (
Name text,
Rank bigint,
rowid bigint GENERATED BY DEFAULT AS IDENTITY
(BIT_REVERSED_POSITIVE) NOT NULL HIDDEN,
PRIMARY KEY (rowid)
);
如果先前建立資料表時未定義主鍵,就無法再為該資料表新增主鍵。
由於 Spanner 不允許資料表中有兩個同名資料欄,因此您無法在先前建立的資料表中定義名為 rowid 的新非鍵資料欄,除非您定義主鍵。下列查詢會傳回錯誤:
GoogleSQL
CREATE TABLE Singers (
rowid INT64,
Name STRING(MAX),
);
PostgreSQL
CREATE TABLE Singers (
rowid bigint,
Name text
);
Spanner 會將 rowid 欄設為 HIDDEN。這表示資料欄不會出現在 SELECT * 查詢中:
GoogleSQL
SELECT * FROM Singers;
/*-------+------+
| Name | Rank |
+-------+------+
| Alice | 1 |
| David | 2 |
+-------+------*/
PostgreSQL
SELECT * FROM Singers;
/*-------+------+
| Name | Rank |
+-------+------+
| Alice | 1 |
| David | 2 |
+-------+------*/
不過,您可以明確查詢隱藏的 rowid 資料欄:
GoogleSQL
SELECT rowid FROM Singers;
/*---------------------+
| rowid |
+---------------------+
| 3458764513820540928 |
+---------------------*/
PostgreSQL
SELECT rowid FROM Singers;
/*---------------------+
| rowid |
+---------------------+
| 3458764513820540928 |
+---------------------*/
雖然 rowid 資料欄不會出現在 SELECT * 查詢中,但 rowid 資料欄會顯示在 INFORMATION_SCHEMA 資料表中。舉例來說,您可以執行下列查詢:
GoogleSQL
SELECT column_name
FROM information_schema.key_column_usage
WHERE constraint_name LIKE 'PK_%' AND table_name = 'Singers';
/*-------------+
| column_name |
+-------------+
| rowid |
+-------------*/
PostgreSQL
SELECT column_name
FROM information_schema.key_column_usage
WHERE constraint_name LIKE 'PK_%' AND table_name = 'singers';
/*-------------+
| column_name |
+-------------+
| "rowid" |
+-------------*/
將 UUID 和序列當做主鍵預設值的使用情境
UUID 和序列的適用情境包括:
- 新應用程式
- 遷移
以下各節將說明這些情境。
新應用程式
如果是新版應用程式,Spanner 建議使用通用唯一識別碼 (UUID) 做為主鍵。詳情請參閱「使用通用唯一識別碼 (UUID)」。
如果現有應用程式需要 GoogleSQL 中的 INT64 鍵,或 PostgreSQL 中的 bigint 鍵,Spanner 提供位元反轉正序結構定義物件 (PostgreSQL 或
GoogleSQL)。
遷移
如要將資料表遷移至 Spanner,可以選擇下列做法:
- 如果您在來源資料庫中使用 UUID,在 Spanner 上,您可以將 UUID 型別資料欄和產生的 UUID 函式 ( GoogleSQL 或 PostgreSQL) 做為預設值。
- 如果您使用整數主鍵,且應用程式只需要主鍵是唯一的,則可以在
INT64中使用主鍵資料欄,並使用位元反轉的正序做為主鍵的預設值。請參閱「遷移位元反轉鍵資料欄」。 - 如果您使用單調遞增鍵 (例如 PostgreSQL
SERIAL型別或 MySQLAUTO_INCREMENT屬性),且需要在 Spanner 上使用新的單調遞增鍵,則可使用複合索引鍵,因為 Spanner 不支援產生單調遞增值。詳情請參閱「交換鍵的順序」和「雜湊唯一鍵並將寫入資料分散到邏輯資料分割」。 - 如果應用程式會手動反轉 GoogleSQL 中的
INT64鍵或 PostgreSQL 中的bigint鍵,您可以使用反轉位元的正序 (GoogleSQL 或 PostgreSQL),讓系統為您產生新的鍵值。詳情請參閱「遷移位元反轉鍵欄」一文。
後續步驟
- 進一步瞭解如何搭配精細存取權控管機制使用序列。
- 瞭解 GoogleSQL 或 PostgreSQL 的 DDL
SEQUENCE陳述式。 - 瞭解 GoogleSQL 或 PostgreSQL 中的序列函式。
- 瞭解
INFORMATION_SCHEMAGoogleSQL 或 PostgreSQL 中的序列。 - 瞭解
INFORMATION_SCHEMA中 GoogleSQL 的序列選項。