迁移主键策略

本文档介绍了将主键从数据库表迁移到 Cloud 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 列属性。
  • 从使用位反转键的源表进行迁移。可能是源数据库是 Cloud Spanner,您可以在其中使用向导的位反转序列值来创建键值对。

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

迁移 UUID 键列

如果源表使用的是 UUID 列,则可以将该列转换为字符串类型,根据 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)
);

迁移顺序键列

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

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;

迁移位反转键列

如果您已对键值对位反转以避免源数据库中出现热点问题,您也可以使用 Cloud Spanner 位反转正向序列(GoogleSQLPostgreSQL)继续生成这些值。为了避免生成重复值,您可以将序列配置为从自定义数字开始其计数器。

例如,如果您将数字 1 反转到 1000 以生成主键值对,则 Cloud 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;