Migrar do PostgreSQL para o Cloud Spanner (dialeto GoogleSQL)

Nesta página, você encontra orientações sobre como migrar um banco de dados PostgreSQL de código aberto para o Cloud Spanner.

A migração envolve as seguintes tarefas:

  • Mapeamento de um esquema do PostgreSQL para um esquema do Spanner.
  • Criar uma instância, um banco de dados e um esquema do Spanner.
  • Refatorar o aplicativo para funcionar com o banco de dados do Spanner.
  • Como migrar os dados
  • Como verificar o novo sistema e movê-lo para o status de produção

Nesta página, também apresentamos alguns esquemas de exemplo usando tabelas do banco de dados PostgreSQL do MusicBrainz.

Mapear seu esquema do PostgreSQL para o Spanner

A primeira etapa para mover um banco de dados do PostgreSQL para o Spanner é determinar quais alterações de esquema você precisa fazer. Use pg_dump para criar instruções de linguagem de definição de dados (DDL) que definem os objetos no banco de dados do PostgreSQL e, em seguida, modifique-as conforme descrito nas seções a seguir. Depois de atualizar as instruções DDL, use-as para criar o banco de dados em uma instância do Spanner.

Tipos de dados

A tabela a seguir descreve como os tipos de dados do PostgreSQL são mapeados para os tipos de dados do Spanner. Atualize os tipos de dados nas instruções DDL de tipos de dados do PostgreSQL para os tipos de dados do Spanner.

PostgreSQL Spanner
Bigint

int8

INT64
Bigserial

serial8

INT64
bit [ (n) ] ARRAY<BOOL>
bit varying [ (n) ]

varbit [ (n) ]

ARRAY<BOOL>
Boolean

bool

BOOL
box ARRAY<FLOAT64>
bytea BYTES
character [ (n) ]

char [ (n) ]

STRING
character varying [ (n) ]

varchar [ (n) ]

STRING
cidr STRING, usando a notação padrão CIDR.
circle ARRAY<FLOAT64>
date DATE
double precision

float8

FLOAT64
inet STRING
Integer

int

int4

INT64
interval[ fields ] [ (p) ] INT64 se armazenar o valor em milissegundos, ou STRING se armazenar o valor em um formato de intervalo definido pelo aplicativo.
json STRING
jsonb JSON
line ARRAY<FLOAT64>
lseg ARRAY<FLOAT64>
macaddr STRING, usando a notação padrão de endereço MAC.
money INT64 ou STRING para números de precisão arbitrária.
numeric [ (p, s) ]

decimal [ (p, s) ]

No PostgreSQL, os tipos de dados NUMERIC e DECIMAL são compatíveis com até 217 dígitos de precisão e 214-1 de escala, conforme definido na coluna declaração.O tipo de dados

do Spanner NUMERIC aceita até 38 dígitos de precisão e 9 dígitos decimais de escala.

Se você precisar de mais precisão, consulte Como armazenar dados numéricos de precisão arbitrária para mecanismos alternativos.
path ARRAY<FLOAT64>
pg_lsn Esse tipo de dados é específico do PostgreSQL, por isso não há um equivalente do Spanner.
point ARRAY<FLOAT64>
polygon ARRAY<FLOAT64>
Real

float4

FLOAT64
Smallint

int2

INT64
Smallserial

serial2

INT64
Serial

serial4

INT64
text STRING
time [ (p) ] [ without time zone ] STRING, usando a notação HH:MM:SS.sss.
time [ (p) ] with time zone

timetz

STRING, usando a notação HH:MM:SS.sss+ZZZZ. Como alternativa, isso pode ser dividido em duas colunas, uma do tipo TIMESTAMP e outra contendo o fuso horário.
timestamp [ (p) ] [ without time zone ] Não há equivalente. Você pode armazenar STRING ou TIMESTAMP a seu critério.
timestamp [ (p) ] with time zone

timestamptz

TIMESTAMP
tsquery Não há equivalente. Em vez disso, defina um mecanismo de armazenamento no aplicativo.
tsvector Não há equivalente. Em vez disso, defina um mecanismo de armazenamento no aplicativo.
txid_snapshot Não há equivalente. Em vez disso, defina um mecanismo de armazenamento no aplicativo.
uuid STRING ou BYTES
xml STRING

Chaves primárias

Para as tabelas no banco de dados do Spanner que você anexa com frequência, evite usar chaves primárias que aumentam ou diminuem monotonicamente, porque essa abordagem causa pontos de acesso durante as gravações. Em vez disso, modifique as instruções CREATE TABLE DDL para que usem estratégias de chaves primárias compatíveis. Se você estiver usando um recurso do PostgreSQL, como um tipo ou função de dados UUID, tipos de dados SERIAL, coluna ou sequência IDENTITY, poderá utilizar as estratégias de migração de chaves geradas automaticamente que recomendamos.

Depois de designar a chave primária, não é possível adicionar ou remover uma coluna de chave primária ou alterar um valor de chave primária posteriormente sem excluir e recriar a tabela. Para mais informações sobre como designar a chave primária, consulte Esquema e modelo de dados – chaves primárias.

Durante a migração, talvez seja necessário manter algumas chaves inteiras atuais que aumentem constantemente. Se precisar manter esses tipos de chaves em uma tabela que é sempre atualizada com muitas operações nessas chaves, evite a criação de pontos de acesso prefixando a chave atual com um número pseudoaleatório. Essa técnica faz com que o Spanner redistribua as linhas. Consulte O que os DBAs precisam saber sobre o Spanner, parte 1: chaves e índices para mais informações sobre como usar essa abordagem.

Chaves estrangeiras e integridade referencial

Saiba mais sobre o suporte a chaves estrangeiras no Spanner.

Índices

Os índices de árvore B do PostgreSQL são semelhantes aos índices secundários no Spanner. Em um banco de dados do Spanner, você usa índices secundários para indexar colunas comumente pesquisadas para melhor desempenho e para substituir quaisquer restrições UNIQUE especificadas nas tabelas. Por exemplo, se a DDL do PostgreSQL tiver esta instrução:

 CREATE TABLE customer (
    id CHAR (5) PRIMARY KEY,
    first_name VARCHAR (50),
    last_name VARCHAR (50),
    email VARCHAR (50) UNIQUE
 );

Você usaria esta instrução na DDL do Spanner:

CREATE TABLE customer (
   id STRING(5),
   first_name STRING(50),
   last_name STRING(50),
   email STRING(50)
   ) PRIMARY KEY (id);

CREATE UNIQUE INDEX customer_emails ON customer(email);

Para encontrar os índices de qualquer uma das tabelas do PostgreSQL, execute o meta-comando \di em psql.

Depois de determinar os índices necessários, adicione instruções CREATE INDEX para criá-los. Siga as orientações em Como criar índices.

O Spanner implementa índices como tabelas. Portanto, a indexação de colunas que crescem constantemente (como aquelas que contêm dados TIMESTAMP) pode causar um ponto de acesso. Consulte O que os DBAs precisam saber sobre o Spanner, parte 1: chaves e índices para mais informações sobre métodos para evitar pontos de acesso.

Verificar restrições

Saiba mais sobre suporte à restrição de CHECK no Spanner.

Outros objetos de banco de dados

É preciso criar a funcionalidade dos seguintes objetos na lógica do aplicativo:

  • Visualizações
  • Acionadores
  • Procedimentos armazenados
  • Funções definidas pelo usuário (UDFs)
  • Colunas que usam tipos de dados serial como geradores de sequência

Tenha as seguintes dicas em mente ao migrar essa funcionalidade para a lógica do aplicativo:

Criar sua instância do Spanner

Depois de atualizar suas instruções DDL para atender aos requisitos de esquema do Spanner, use-as para criar o banco de dados no Spanner.

  1. Crie uma instância do Spanner. Siga as orientações em Instâncias para determinar a configuração regional correta e a capacidade de computação compatível com suas metas de desempenho.

  2. Crie o banco de dados usando o console do Google Cloud ou a ferramenta de linha de comando gcloud:

Console

  1. Acessar a página "Instâncias"
  2. Clique no nome da instância na qual você quer criar o banco de dados de exemplo para abrir a página Detalhes da instância.
  3. Clique em Criar banco de dados.
  4. Digite um nome para o banco de dados e clique em Continuar.
  5. Na seção Definir seu esquema de banco de dados, ative o controle Editar como texto.
  6. Copie e cole as instruções DDL no campo Instruções DDL.
  7. Clique em Criar.

gcloud

  1. Instale a CLI gcloud.
  2. Use o comando gcloud spanner databases create para criar o banco de dados:
    gcloud spanner databases create DATABASE_NAME --instance=INSTANCE_NAME
    --ddl='DDL1' --ddl='DDL2'
    
  • DATABASE_NAME é o nome do banco de dados.
  • INSTANCE_NAME é a instância do Spanner que você criou.
  • DDLn são as instruções DDL modificadas.

Depois de criar o banco de dados, siga as instruções em Como aplicar papéis do IAM para criar contas de usuário e conceder permissões à instância e ao banco de dados do Spanner.

Refatorar os aplicativos e as camadas de acesso aos dados

Além do código necessário para substituir os objetos de banco de dados anteriores, é preciso adicionar a lógica do aplicativo para processar a funcionalidade a seguir:

  • Geração de hash de chaves primárias para gravações, em tabelas com altas taxas de gravação para chaves sequenciais.
  • Validação de dados, ainda não coberta por restrições CHECK.
  • Verificações de integridade referencial que ainda não são cobertas pela chave externa, intercalação de tabelas ou lógica de aplicativo, incluindo a funcionalidade executada por acionadores no esquema do PostgreSQL.

Recomendamos usar o seguinte processo ao refatorar:

  1. Encontre todo o código do aplicativo que acessa o banco de dados e refatore-o em um único módulo ou biblioteca. Desse modo, você sabe exatamente que código acessa o banco de dados e, portanto, o código exato que precisa ser modificado.
  2. Escreva um código que execute leituras e gravações na instância do Spanner, fornecendo funcionalidade paralela ao código original que lê e grava no PostgreSQL. Durante as gravações, atualize toda a linha, não apenas as colunas que foram alteradas, para garantir que os dados no Spanner sejam idênticos aos do PostgreSQL.
  3. Escreva um código que substitua a funcionalidade dos objetos e funções de banco de dados que não estão disponíveis no Spanner.

Migrar dados

Depois de criar o banco de dados do Spanner e refatorar o código do aplicativo, é possível migrar os dados para o Spanner.

  1. Use o comando COPY do PostgreSQL para descarregar os dados para arquivos .csv.
  2. Faça upload dos arquivos .csv para o Cloud Storage.

    1. Crie um bucket do Cloud Storage.
    2. No console do Cloud Storage, clique no nome do bucket para abrir o navegador dele.
    3. Clique em Enviar arquivos.
    4. Navegue até o diretório que contém os arquivos .csv e selecione-os.
    5. Clique em Abrir.
  3. Crie um aplicativo para importar dados para o Spanner. Este aplicativo pode usar Fluxo de dados ou as bibliotecas de cliente diretamente. Siga as orientações em Práticas recomendadas de carregamento de dados em lote para ter o melhor desempenho.

Testes

Teste todas as funções do aplicativo na instância do Spanner para verificar se elas funcionam conforme o esperado. Execute cargas de trabalho no nível de produção para garantir que o desempenho atenda às suas necessidades. Atualize a capacidade de computação conforme necessário para atingir suas metas de desempenho.

Mover para o novo sistema

Depois de concluir o teste inicial do aplicativo, ative o novo sistema usando um dos seguintes processos. A migração off-line é o método mais simples. No entanto, essa abordagem torna o aplicativo indisponível por um período e não permite nenhum caminho de reversão se você encontrar problemas de dados posteriormente. Para executar uma migração off-line:

  1. Exclua todos os dados do banco de dados do Spanner.
  2. Encerre o aplicativo que tem como destino o banco de dados PostgreSQL.
  3. Exporte todos os dados do banco de dados PostgreSQL e importe-os para o banco de dados do Spanner, conforme descrito em Como migrar dados.
  4. Inicie o aplicativo que tem como destino o banco de dados do Spanner.

    Fluxo de dados de migração off-line.

A migração em tempo real é possível e requer extensas alterações no aplicativo para oferecer suporte à migração.

Exemplos de migração de esquema

Estes exemplos mostram as instruções de CREATE TABLE para várias tabelas no esquema do banco de dados MusicBrainz do PostgreSQL. Cada exemplo inclui os esquemas do PostgreSQL e do Spanner.

tabela artist_credit

Versão do PostgreSQL:

CREATE TABLE artist_credit (
  id SERIAL,
  name VARCHAR NOT NULL,
  artist_count SMALLINT NOT NULL,
  ref_count INTEGER DEFAULT 0,
  created TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

Versão do Spanner:

CREATE TABLE artist_credit (
  hashed_id STRING(4),
  id INT64,
  name STRING(MAX) NOT NULL,
  artist_count INT64 NOT NULL,
  ref_count INT64,
  created TIMESTAMP OPTIONS (
     allow_commit_timestamp = true
  ),
) PRIMARY KEY(hashed_id, id);

tabela recording

Versão do PostgreSQL:

CREATE TABLE recording (
  id SERIAL,
  gid UUID NOT NULL,
  name VARCHAR NOT NULL,
  artist_credit INTEGER NOT NULL, -- references artist_credit.id
  length INTEGER CHECK (length IS NULL OR length > 0),
  comment VARCHAR(255) NOT NULL DEFAULT '',
  edits_pending INTEGER NOT NULL DEFAULT 0 CHECK (edits_pending >= 0),
  last_updated TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  video BOOLEAN NOT NULL DEFAULT FALSE
);

Versão do Spanner:

CREATE TABLE recording (
  hashed_id STRING(36),
  id INT64,
  gid STRING(36) NOT NULL,
  name STRING(MAX) NOT NULL,
  artist_credit_hid STRING(36) NOT NULL,
  artist_credit_id INT64 NOT NULL,
  length INT64,
  comment STRING(255) NOT NULL,
  edits_pending INT64 NOT NULL,
  last_updated TIMESTAMP OPTIONS (
     allow_commit_timestamp = true
  ),
  video BOOL NOT NULL,
) PRIMARY KEY(hashed_id, id);

tabela recording-alias

Versão do PostgreSQL:

CREATE TABLE recording_alias (
  id SERIAL, --PK
  recording INTEGER NOT NULL, -- references recording.id
  name VARCHAR NOT NULL,
  locale TEXT,
  edits_pending INTEGER NOT NULL DEFAULT 0 CHECK (edits_pending >=0),
  last_updated TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  type INTEGER, -- references recording_alias_type.id
  sort_name VARCHAR NOT NULL,
  begin_date_year SMALLINT,
  begin_date_month SMALLINT,
  begin_date_day SMALLINT,
  end_date_year SMALLINT,
  end_date_month SMALLINT,
  end_date_day SMALLINT,
  primary_for_locale BOOLEAN NOT NULL DEFAULT false,
  ended BOOLEAN NOT NULL DEFAULT FALSE
  -- CHECK constraint skipped for brevity
);

Versão do Spanner:

CREATE TABLE recording_alias (
  hashed_id STRING(36)  NOT NULL,
  id INT64  NOT NULL,
  alias_id INT64,
  name STRING(MAX)  NOT NULL,
  locale STRING(MAX),
  edits_pending INT64  NOT NULL,
  last_updated TIMESTAMP NOT NULL OPTIONS (
     allow_commit_timestamp = true
  ),
  type INT64,
  sort_name STRING(MAX)  NOT NULL,
  begin_date_year INT64,
  begin_date_month INT64,
  begin_date_day INT64,
  end_date_year INT64,
  end_date_month INT64,
  end_date_day INT64,
  primary_for_locale BOOL NOT NULL,
  ended BOOL NOT NULL,
) PRIMARY KEY(hashed_id, id, alias_id),
 INTERLEAVE IN PARENT recording ON DELETE NO ACTION;