主键默认值管理

本页介绍了使用默认值表达式在表中生成主键值的策略。这些策略具有以下优势:

  • 防止热点
  • 简化从其他数据库迁移
  • 将密钥逻辑封装在数据库中,这样您就不必担心在应用中管理它

您可以在包含 DEFAULT 表达式的列中使用以下策略:

  • 用于生成 UUID 版本 4 值的 UUID 函数。
  • 具有 bit_reversed_positive 选项的架构对象 SEQUENCESEQUENCE 适用于 GoogleSQL 和 PostgreSQL。

自动生成主键的方法

通用唯一标识符 (UUID)

Spanner 可以自动生成 UUID 版本 4 字符串,以用作主键。UUID 非常适用于新应用和包含许多行的表。它们大致均匀地分布在键空间中,这可防止出现大规模热点。UUID 生成可以创建大量值 (2122),并且每个值实际上都是唯一的。例如,您需要 2.71×1018 个值,才能使冲突概率达到 50%,也就是说,需要 86 年的时间,每秒生成 10 亿个值。这样可以确保在大型表中使用时,值是唯一的。无论是在数据库还是在客户端中生成 UUID,它们都是唯一的。我们建议您尽可能使用 UUID。如果客户端生成的 UUID 按 RFC 4122 规定序列化为小写形式,则您可以在同一表中安全地混合使用客户端生成的 UUID 和 Spanner 生成的 UUID。

对于需要默认值的列,您可以使用 GENERATE_UUID 函数生成这些值。以下示例展示了如何创建一个表,其中 FanId 键列的值列将 GENERATE_UUID 作为默认值。该示例为 GoogleSQL STRING 和 PostgreSQL varchar 属性使用了 36 个字符,因为 UUID 有 36 个字符。当您使用 INSERT with THEN RETURN 语句插入 Fans 表时,GENERATE_UUID 会为 FanId 生成并返回 UUID 值。

GoogleSQL

CREATE TABLE Fans (
  FanId STRING(36) DEFAULT (GENERATE_UUID()),
  Name STRING(MAX),
) PRIMARY KEY (FanId);

PostgreSQL

CREATE TABLE Fans (
  FanId varchar(36) DEFAULT spanner.generate_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

如需详细了解 GENERATE_UUID() 函数,请参阅 GoogleSQLPostgreSQL 参考页面。

IDENTITY

借助 IDENTITY 列,您可以为键列和非键列自动生成整数值。IDENTITY 列无需用户手动维护底层序列,也不需要管理该列与底层序列之间的关系。删除自动生成的标识列时,系统也会自动删除底层序列。

您可以通过以下方式使用 IDENTITY 列:在生成序列时提供起始整数值,或让 Spanner 为您生成整数序列。如需提供起始整数值,您必须使用 START COUNTER WITH 选项并使用正 INT64 起始值。Spanner 使用此值为其自动生成的内部序列计数器设置下一个值,并在将该值插入此列之前对其进行位反转。

在 Spanner 中,GoogleSQL 和 PostgreSQL 均支持 IDENTITY 列。

GoogleSQL

以下示例展示了如何在使用 CREATE TABLE 命令创建新表时,使用 IDENTITY 列为 SingerId 创建自动生成的整数主键列:

CREATE TABLE Singers (
  SingerId INT64 GENERATED BY DEFAULT AS IDENTITY (BIT_REVERSED_POSITIVE),
  Name STRING(MAX),
  Rank INT64
) PRIMARY KEY (SingerId);

您还可以使用 START_WITH_COUNTER 选项指定该列的计数器起始值。在以下示例中,系统会为 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

以下示例展示了如何在使用 CREATE TABLE 命令创建新表时,使用 IDENTITY 列为 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)
);

位反转序列

按位翻转的序列是一种架构对象,用于生成一系列整数并对其进行按位翻转。此对象会对专用内部 Spanner 计数器进行位反转,以确保唯一性。生成的二进制反转值有助于在主键中使用时避免大规模热点问题。

在 Spanner 中,您可以使用 SEQUENCE DDL 语句和 bit_reversed_positive 属性来创建、修改或删除生成位反转正值的序列(GoogleSQLPostgreSQL)。

每个序列都会维护一组内部计数器,并使用这些计数器生成值。序列计数器为位反转算法提供输入。

如果您使用 DEFAULT 表达式定义列,并且该表达式使用 GoogleSQL GET-NEXT-SEQUENCE-VALUE 或 PostgreSQL nextval 函数作为默认值,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 ('Melissa Garcia')
THEN RETURN SingerId;

PostgreSQL

INSERT INTO Singers (name) VALUES ('Melissa Garcia')
RETURNING (SingerId);

此语句会返回类似于以下内容的结果:

SingerId
3458764513820540928

将 UUID 和序列用作主键默认值的场景

UUID 和序列的场景包括:

  • 新应用
  • 迁移

以下部分介绍了每种情形。

新应用

如果您的现有应用需要在 GoogleSQL 中使用 INT64 键,或在 PostgreSQL 中使用 bigint 键,Spanner 会提供位反转的正序架构对象(PostgreSQLGoogleSQL)。否则,对于新应用,我们建议您使用通用唯一标识符 (UUID)。如需了解详情,请参阅使用通用唯一标识符 (UUID)

迁移

如需将表迁移到 Spanner,您可以选择以下几种方式:

  • 如果您在源数据库中使用 UUID,则在 Spanner 中,您可以使用 STRING 类型中的键列和 GENERATE_UUID() 函数(GoogleSQLPostgreSQL)作为其默认值。
  • 如果您使用的是整数主键,并且您的应用只需要主键是唯一的,则可以在 INT64 中使用键列,并将二进制反转的正序作为主键的默认值。请参阅迁移位反转的键列
  • Spanner 不支持生成单调递增值的方法。

    如果您使用的是单调键(例如 PostgreSQL SERIAL 类型或 MySQL AUTO_INCREMENT 属性),并且需要在 Spanner 上使用新的单调键,则可以使用复合键。请参阅交换键的顺序对唯一键进行哈希处理并将写入分布到逻辑碎片中

  • 如果您的应用在 GoogleSQL 中手动对 INT64 密钥进行位反转,或在 PostgreSQL 中对 bigint 密钥进行位反转,您可以使用位反转的正序(GoogleSQLPostgreSQL),让它为您生成新的密钥值。请参阅迁移位反转的键列

后续步骤