迁移主键

本页面介绍了如何将主键从源数据库表迁移到 Spanner GoogleSQL 方言数据库和 PostgreSQL 方言数据库。在执行本页面上的过程之前,请先查看主键迁移概览

准备工作

  • 如需获得将主键迁移到 Spanner 所需的权限,请让您的管理员为您授予实例的 Cloud Spanner Database Admin (roles/spanner.databaseAdmin) IAM 角色。

迁移自动生成的顺序键

如果您要从使用顺序单调键(例如 MySQL 中的 AUTO_INCREMENT、PostgreSQL 中的 SERIAL 或是 SQL Server 或 Oracle 中的标准 IDENTITY 类型)的数据库进行迁移,请考虑以下简要的迁移策略:

  1. 在 Spanner 中,使用整数主键复制源数据库中的表结构。
  2. 对于 Spanner 中包含顺序值的每列,请创建一个序列并将 GET_NEXT_SEQUENCE_VALUE ( GoogleSQLPostgreSQL) 函数分配为列的默认值。
  3. 将包含原始键的现有数据从源数据库迁移到 Spanner。请考虑使用 Spanner 迁移工具Dataflow 模板
    1. (可选)您可以为任何依赖表建立外键限制条件
  4. 在插入新数据之前,请调整 Spanner 序列以跳过现有键值的范围。
  5. 插入新数据,让序列可以自动生成唯一键。

示例迁移工作流

以下代码使用一个 SEQUENCE 对象在 Spanner 中定义表结构和相关序列,并将该对象设置为目标表的默认主值:

GoogleSQL

CREATE SEQUENCE singer_id_sequence OPTIONS (
     SequenceKind = 'bit_reversed_positive'
  );

CREATE TABLE Singers (
     SingerId INT64 DEFAULT
     (GET_NEXT_SEQUENCE_VALUE(SEQUENCE SingerIdSequence)),
     Name STRING(1024),
     Biography STRING(MAX),
  ) PRIMARY KEY (SingerId);

CREATE TABLE Albums (
     AlbumId INT64,
     SingerId INT64,
     AlbumName STRING(1024),
     SongList STRING(MAX),
     CONSTRAINT FK_singer_album
     FOREIGN KEY (SingerId)
       REFERENCES Singers (SingerId)
  ) PRIMARY KEY (AlbumId);

PostgreSQL

CREATE SEQUENCE SingerIdSequence BIT_REVERSED_POSITIVE;

CREATE TABLE Singers (
  SingerId BIGINT DEFAULT nextval('SingerIdSequence') PRIMARY KEY,
  Name VARCHAR(1024) NOT NULL,
  Biography TEXT
);

CREATE TABLE Albums (
  AlbumId BIGINT PRIMARY KEY,
  SingerId BIGINT,
  AlbumName VARCHAR(1024),
  SongList TEXT,
  CONSTRAINT FK_singer_album FOREIGN KEY (SingerId) REFERENCES Singers (SingerId)
);

bit_reversed_positive 选项表示序列生成的值属于 INT64 类型,大于零,且不是顺序的。

当您将现有行从源数据库迁移到 Spanner 时,主键保持不变。

对于未指定主键的新插入,Spanner 会通过调用 GET_NEXT_SEQUENCE_VALUEGoogleSQLPostgreSQL)函数来自动检索新值。

这些值在 [1, 263] 范围中均匀分布,可能会与现有键发生冲突。如需避免这种情况,您可以使用 ALTER_SEQUENCEGoogleSQLPostgreSQL)配置序列,以跳过现有键涵盖的值范围。

假设 singers 表是从 PostgreSQL 迁移的,其主键 singer_id 属于 SERIAL 类型。以下 PostgreSQL 显示了您的源数据库 DDL:

PostgreSQL

CREATE TABLE Singers (
SingerId SERIAL PRIMARY KEY,
Name varchar(1024),
Biography varchar
);

主键值单调递增。迁移后,您可以在 Spanner 中检索主键 singer_id 的最大值。请在 Spanner 中使用以下代码:

GoogleSQL

SELECT MAX(SingerId) FROM Singers;

PostgreSQL

SELECT MAX(SingerId) FROM Singers;

假设返回值为 20,000。您可以配置 Spanner 序列以跳过范围 [1, 21000]。额外的 1,000 用作缓冲区,以便容纳在初始迁移后对源数据库进行的写入操作。在 Spanner 中生成的新键不会与源 PostgreSQL 数据库中生成的主键范围冲突。请在 Spanner 中使用以下代码:

GoogleSQL

ALTER SEQUENCE SingerIdSequence SET OPTIONS (
skip_range_min = 1,
skip_range_max = 21000
);

PostgreSQL

ALTER SEQUENCE SingerIdSequence SKIP RANGE 1 21000;

使用 Spanner 和源数据库

您可以使用跳过范围概念来支持 Spanner 或源数据库生成主键的场景,例如在迁移割接期间,用于在任一方向上启用复制以实现灾难恢复。

为了支持此功能,两个数据库会生成主键,并且数据会在它们之间同步。您可以将每个数据库配置为在不重叠的键范围内创建主键。为源数据库定义范围时,您可以将 Spanner 序列配置为跳过该范围。

例如,在音乐曲目应用迁移完成后,将数据从 PostgreSQL 复制到 Spanner,以缩短割接所需的时间。

在 Spanner 上更新并测试应用后,您可以停止使用源 PostgreSQL 数据库并使用 Spanner,使其成为更新和新主键的记录系统。当 Spanner 接管后,您可以使数据库之间的数据流反向流入 PostgreSQL 实例。

假设您的源 PostgreSQL 数据库使用 SERIAL 主键,这些是 32 位有符号整数。Spanner 主键是更大的 64 位数字。在 PostgreSQL 中,将主键列更改为 64 位列或 bigint。请对源 PostgreSQL 数据库使用以下代码:

PostgreSQL

ALTER TABLE Singers ALTER COLUMN SingerId TYPE bigint;

您可以对源 PostgreSQL 数据库中的表设置 CHECK 限制条件,以确保 SingerId 主键的值始终小于或等于 231-1

请对源 PostgreSQL 数据库使用以下代码:

PostgreSQL

ALTER TABLE Singers ADD CHECK (SingerId <= 2147483647);

在 Spanner 中,我们可以更改序列以跳过 [1, 231-1] 范围。

请在 Spanner 中使用以下代码:

GoogleSQL

ALTER SEQUENCE SingerIdSequence SET OPTIONS (
skip_range_min = 1,
skip_range_max = 2147483647 -- 231-1
);

PostgreSQL

ALTER SEQUENCE SingerIdSequence SKIP RANGE 1 2147483648;

源 PostgreSQL 数据库始终在 32 位整数空间中生成键,而 Spanner 键限制为 64 位整数空间中大于所有 32 位整数值的值。这样可确保两个数据库可以独立生成不冲突的主键。

迁移 UUID 键列

UUIDv4 键实际上具有唯一性,无论它们在何处生成。在其他位置生成的 UUID 键会与在 Spanner 中生成的新 UUID 键集成。

请考虑以下用于将 UUID 键迁移到 Spanner 的简要策略:

  1. 使用包含默认表达式的字符串列在 Spanner 中定义 UUID 键。使用 GENERATE_UUID() 函数(GoogleSQLPostgreSQL)。
  2. 从源系统导出数据,并将 UUID 键序列化为字符串。
  3. 将主键导入 Spanner。
  4. 可选:启用外键。

以下是一个示例迁移工作流:

在 Spanner 中,将 UUID 主键列定义为 STRINGTEXT 类型,并将 GENERATE_UUID()GoogleSQLPostgreSQL)分配为其默认值。将源数据库中的所有数据迁移到 Spanner。迁移后,在插入新行时,Spanner 会调用 GENERATE_UUID() 为主键生成新 UUID 值。例如,在表 FanClubs 中插入新行时,主键 FanClubId 会获取 UUIDv4 值。请在 Spanner 中使用以下代码:

GoogleSQL

CREATE TABLE Fanclubs (
FanClubId STRING(36) DEFAULT (GENERATE_UUID()),
ClubName STRING(1024),
) PRIMARY KEY (FanClubId);

INSERT INTO FanClubs (ClubName) VALUES ("SwiftFanClub");

PostgreSQL

CREATE TABLE FanClubs (
  FanClubId TEXT DEFAULT spanner.generate_uuid() PRIMARY KEY,
  ClubName VARCHAR(1024)
);

INSERT INTO FanClubs (ClubName) VALUES ('SwiftFanClub');

迁移您自己的主键

您的应用可能会依赖主键顺序来确定数据的新旧程度或是对新创建的数据进行排序。如需在 Spanner 中使用外部生成的顺序键,您可以创建一个复合键,将均匀分布的值(例如哈希)作为第一个组成部分,并将顺序键作为第二个组成部分来进行组合。这样一来,您便可以保留顺序键值,而无需大规模创建热点。请考虑以下迁移工作流:

假设您需要将具有 AUTO_INCREMENT 主键的 MySQL 表 students 迁移到 Spanner。请在源 MySQL 数据库中使用以下代码:

MySQL

CREATE TABLE Students (
StudentId INT NOT NULL AUTO_INCREMENT,
Info VARCHAR(2048),
PRIMARY KEY (StudentId)
);

在 Spanner 中,您可以通过创建 StudentId 列的哈希来添加生成的列 StudentIdHash。例如:

StudentIdHash = FARM_FINGERPRINT(CAST(StudentId AS STRING))

您可以在 Spanner 中使用以下代码:

GoogleSQL

CREATE TABLE student (
  StudentIdHash INT64 AS (FARM_FINGERPRINT(cast(StudentId as string))) STORED,
  StudentId INT64 NOT NULL,
  Info STRING(2048),
) PRIMARY KEY(StudentIdHash, StudentId);

PostgreSQL

CREATE TABLE Student (
  StudentIdHash bigint GENERATED ALWAYS AS
  (FARM_FINGERPRINT(cast(StudentId AS varchar))) STORED,
  StudentId bigint NOT NULL,
  Info varchar(2048),
  PRIMARY KEY (StudentIdHash, StudentId)
);

迁移顺序键列

如果您的源数据库系统为键列生成顺序值,您可以在 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;

如果您使用 IDENTITY为键列自动生成整数值,则可以设置跳过范围:

GoogleSQL

如需设置跳过范围,请使用 GENERATED BY DEFAULT AS IDENTITY 命令:

ALTER DATABASE db SET OPTIONS (
  default_sequence_kind = 'bit_reversed_positive',
);

CREATE TABLE MyFirstTable (
  Id INT64 GENERATED BY DEFAULT AS IDENTITY (SKIP RANGE 1, 4294967296),
  Name STRING(MAX),
) PRIMARY KEY (Id);

ALTER TABLE MyFirstTable ALTER COLUMN Id ALTER IDENTITY SET SKIP RANGE 1, 4294967296;

PostgreSQL

如需设置跳过范围,请使用 GENERATED BY DEFAULT AS IDENTITY 命令:

ALTER DATABASE db
    SET spanner.default_sequence_kind = 'bit_reversed_positive';

CREATE TABLE MyFirstTable (
  Id bigint GENERATED BY DEFAULT AS IDENTITY (SKIP RANGE 1 4294967296),
  Name text,
  PRIMARY KEY (Id)
);

ALTER TABLE MyFirstTable ALTER COLUMN Id SET 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;

如果您使用 IDENTITY 列为键列自动生成整数值,则可以设置起始计数器

GoogleSQL

如需设置起始计数器,请使用 GENERATED BY DEFAULT AS IDENTITY 命令:

ALTER DATABASE db SET OPTIONS (
  default_sequence_kind = 'bit_reversed_positive',
);

CREATE TABLE MyFirstTable (
  Id INT64 GENERATED BY DEFAULT AS IDENTITY (START COUNTER WITH 11000),
  Name STRING(MAX),
) PRIMARY KEY (Id);

ALTER TABLE MyFirstTable ALTER COLUMN Id ALTER IDENTITY RESTART COUNTER WITH 11000;

PostgreSQL

如需设置起始计数器,请使用 GENERATED BY DEFAULT AS IDENTITY 命令:

ALTER DATABASE db
    SET spanner.default_sequence_kind = 'bit_reversed_positive';

CREATE TABLE MyFirstTable (
  Id bigint GENERATED BY DEFAULT AS IDENTITY (START COUNTER WITH 11000),
  Name text,
  PRIMARY KEY (Id)
);

ALTER TABLE MyFirstTable ALTER COLUMN Id RESTART COUNTER WITH 11000;

后续步骤