迁移主键策略

本文档介绍了将主键从数据库表迁移到 Spanner 以避免热点问题的策略。热点是指集中于单个节点上的操作,这会将写入吞吐量降低到该节点的容量,无法受益于在 Spanner 节点之间对所有写入操作进行负载均衡。使用单调递增或递减列作为主键的第一部分(例如常规序列或时间戳)是出现热点的最常见原因。

整体策略

在 Spanner 中,必须存储多个行的每个表都必须具有一个主键,该主键由表的一个或多个列组成。表的主键唯一标识表中的每一行,并且 Spanner 使用主键对表中的行进行排序。由于 Spanner 高度分布,因此您可以使用以下技术生成唯一的主键值并降低出现热点的风险:

  • 使用 Spanner 支持的热点自动生成密钥功能(如需了解详情,请参阅迁移自动生成的密钥部分):
    • 使用 GENERATE_UUID() 函数(GoogleSQLPostgreSQL)生成具有 STRING(36) 数据类型的通用唯一标识符(UUID 版本 4)值。RFC 4122 定义了 UUID 版本 4 格式。
    • 使用位反转的正序列(GoogleSQLPostgreSQL)。这样的序列会生成高位已反转的正值,使其均匀分布在正 64 位数字空间中。
  • 交换键的顺序,以便包含单调递增或递减值的列不是键的第一个部分。
  • 创建一个包含唯一键哈希值的列,然后使用该哈希列(或结合使用哈希列和唯一键列)作为主键,对唯一键进行哈希处理并将写入分布到逻辑分片中。这种方法有助于避免热点,因为新行在键空间内的分布更均匀。

为表指定主键后,除非删除并重新创建表,否则之后将无法更改主键。如需详细了解如何指定主键,请参阅架构和数据模型 - 主键

以下是一个为音乐曲目数据库创建表的 DDL 语句示例:

GoogleSQL

CREATE TABLE Singers (
  SingerId   INT64 NOT NULL,
  FirstName  STRING(1024),
  LastName   STRING(1024),
  SingerInfo BYTES(MAX),
  BirthDate  DATE,
) PRIMARY KEY(SingerId);

PostgreSQL

CREATE TABLE Singers (
  SingerId   bigint NOT NULL,
  FirstName  varchar(1024),
  LastName   varchar(1024),
  SingerInfo bytea,
  BirthDate  date,
  PRIMARY KEY(SingerId)
);

迁移自动生成的密钥

本部分介绍了源表已使用自动生成的关键特征的以下场景的策略和示例:

  • 从使用 UUID 主键的源表迁移。
  • 从使用顺序自动生成的整数键的源表迁移。示例包括但不限于序列(各种数据库支持)、IDENTITY 列(各种数据库支持)、PostgreSQL SERIAL 数据类型和 MySQL AUTO_INCREMENT 列属性。
  • 从使用位反转键的源表迁移。源数据库可能是 Spanner,您可以按照对序列值进行位反转指南创建键值。

请务必注意,在所有策略中,Spanner 不会更改从源数据库迁移的数据。您只需更改生成新数据的方法即可。

迁移 UUID 键列

如果源表使用的是 UUID 列,则可以将该列转换为 STRING 类型,根据 RFC 4122 规范将值转换为小写,并将 GENERATE_UUID() 函数(GoogleSQLPostgreSQL)用作列默认值。例如:

GoogleSQL


CREATE TABLE UserAccessLog (
UserId     STRING(36) DEFAULT (GENERATE_UUID()),
...
) PRIMARY KEY (UserId);

PostgreSQL


CREATE TABLE UserAccessLog (
UserId     varchar(36) DEFAULT SPANNER.GENERATE_UUID(),
...
PRIMARY KEY (UserId)
);

迁移顺序键列

如果源数据库系统为键列生成序列值,您可以在 Spanner 架构中使用位反转正序列(GoogleSQLPostgreSQL)来生成在 64 位正整数空间中均匀分布的值。为防止 Spanner 序列生成与迁移值重叠的值,您可以为它定义一个跳过的范围。例如,如果您知道源数据库仅生成 32 位整数,则可以针对以下两个序列跳过 1 到 4,294,967,296 (2^32) 这一范围:

GoogleSQL


CREATE SEQUENCE MyFirstSequence OPTIONS (
  sequence_kind = "bit_reversed_positive",
  skip_range_min = 1,
  skip_range_max = 4294967296
);

ALTER SEQUENCE MySecondSequence SET OPTIONS (
  skip_range_min = 1,
  skip_range_max = 4294967296
);

PostgreSQL


CREATE SEQUENCE MyFirstSequence BIT_REVERSED_POSITIVE 
  SKIP RANGE 1 4294967296;

ALTER SEQUENCE MySecondSequence SKIP RANGE 1 4294967296;

迁移位反转键列

如果您已对键值执行位反转,以避免源数据库中的热点问题,您还可以使用 Spanner 的位反正序列(GoogleSQLPostgreSQL)继续生成这些值。为避免生成重复值,您可以将序列配置为从自定义数字启动计数器。

例如,如果您用 1 到 1000 倒序生成主键值,则 Spanner 序列可以从任何大于 10,000 的数字开始其计数器。(可选)您可以选择一个较大的数值,以便为数据迁移后源数据库中发生的新写入操作预留缓冲区。在以下示例中,计数器从 11,000 开始:

GoogleSQL


CREATE SEQUENCE MyFirstSequence OPTIONS (
  sequence_kind = "bit_reversed_positive",
  start_with_counter = 11000
);

ALTER SEQUENCE MySecondSequence SET OPTIONS (
  start_with_counter = 11000
);

PostgreSQL


CREATE SEQUENCE MyFirstSequence BIT_REVERSED_POSITIVE
  START COUNTER 11000;

ALTER SEQUENCE MySecondSequence RESTART COUNTER 11000;