Migrar do MySQL para o Cloud Spanner

Neste artigo, explicamos como fazer a migração do seu banco de dados OLTP (Online Transactional Processing) do MySQL para o Cloud Spanner.

Restrições de migração

No Cloud Spanner, determinados conceitos são usados de maneira diferente de outras ferramentas de gerenciamento de bancos de dados corporativos, portanto, talvez seja necessário ajustar a arquitetura do seu aplicativo para aproveitar ao máximo os recursos disponíveis. Talvez também seja necessário complementar o Cloud Spanner com outros serviços do Google Cloud para atender às suas necessidades.

Acionadores e procedimentos armazenados

O Cloud Spanner não é compatível com a execução do código do usuário no nível do banco de dados. Por isso, como parte da migração, você precisa mover para o aplicativo a lógica de negócios implementada pelos acionadores e procedimentos armazenados no nível do banco de dados.

Sequências

O Spanner recomenda o uso do UUID versão 4 como método padrão para gerar valores de chave primária. A função GENERATE_UUID() (GoogleSQL, PostgreSQL) retorna valores UUID versão 4 representados como tipo STRING.

Se você precisar gerar valores inteiros, o Cloud Spanner oferece suporte a sequências positivas invertidas em bits (GoogleSQL, PostgreSQL), que produzem valores que distribuem-se uniformemente pelo espaço positivo de números de 64 bits. É possível usar esses números para evitar problemas de ponto de acesso.

Para mais informações, consulte Estratégias de valor padrão das chaves primárias.

Controles de acesso

O Cloud Spanner oferece suporte ao controle de acesso refinado no nível da tabela e da coluna. O controle de acesso refinado para visualizações não é compatível. Para mais informações, consulte Sobre controle de acesso detalhado.

Restrições de validação de dados

O Spanner suporta um conjunto limitado de restrições de validação de dados na camada do banco de dados. Se você precisar de restrições de dados mais complexas, deverá implementá-las na camada do aplicativo.

A tabela a seguir discute os tipos de restrições comumente encontrados nos bancos de dados MySQL e como implementá-los com o Spanner.

Restrição Implementação com o Spanner
Não nulo Restrição de coluna NOT NULL
Único Índice secundário com restrição UNIQUE.
Foreign key (para tabelas normais) Consulte Criar e gerenciar relacionamentos de chave estrangeira.
Ações ON DELETE/ON UPDATE de chaves estrangeiras Possível somente para tabelas intercaladas. Caso contrário, implementada na camada do aplicativo
Verificações de valor e validação por meio de restrições CHECK Consulte Como criar e gerenciar restrições de verificação.
Verificações de valor e validação por meio de acionadores Implementada na camada do aplicativo

Colunas geradas

O Spanner é compatível com colunas geradas, onde o valor da coluna sempre será gerado por uma função fornecida como parte da definição da tabela. Como no MySQL, as colunas geradas não podem ser expressamente definidas como um valor fornecido em uma instrução DML.

As colunas geradas são definidas como parte da definição da coluna durante uma instrução de linguagem de definição de dados (DDL, na sigla em inglês) CREATE TABLE ou ALTER TABLE. A palavra-chave AS é seguida por uma função SQL válida e pela palavra-chave de sufixo STORED obrigatória. A palavra-chave STORED faz parte da especificação ANSI SQL e indica que os resultados da função serão armazenados junto com outras colunas da tabela.

A função SQL, a expressão de geração, pode incluir qualquer expressão, função e operador determinística. Ela pode ser usada em índices secundários ou como uma chave externa.

Para saber mais sobre como gerenciar esse tipo de coluna, consulte Como criar e gerenciar colunas geradas.

Tipos de dados compatíveis

O MySQL e o Spanner são compatíveis com diferentes conjuntos de tipos de dados. A tabela a seguir lista os tipos de dados MySQL e seus equivalentes no Spanner. Para ver definições detalhadas de cada tipo de dados do Spanner, consulte Tipos de dados.

Talvez seja necessário transformar ainda mais os dados conforme descrito na coluna "Observações" para que os dados do MySQL se ajustem ao banco de dados do Spanner. Por exemplo, é possível armazenar um BLOB grande, como um objeto, em um bucket do Cloud Storage, em vez de armazená-lo no banco de dados e depois armazenar a referência do URI ao objeto do Cloud Storage no banco de dados como uma STRING.

Tipo de dados do MySQL Equivalente do Spanner Observações
INTEGER, INT, BIGINT MEDIUMINT, SMALLINT INT64
TINYINT, BOOL, BOOLEAN BOOL, INT64 Os valores TINYINT(1) são usados para representar valores booleanos de "true" (diferente de zero) ou "false" (0).
FLOAT, DOUBLE FLOAT64
DECIMAL, NUMERIC NUMERIC, STRING No MySQL, os tipos de dados NUMERIC e DECIMAL aceitam até um total de 65 dígitos de precisão e escala, conforme definido na declaração da coluna. O tipo de dados NUMERIC do Spanner 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.
BIT BYTES
DATE DATE Tanto o Spanner quanto o MySQL usam o formato "yyyy-mm-dd" para datas. Portanto, nenhuma transformação é necessária. Funções SQL são fornecidas para converter datas em uma string formatada.
DATETIME, TIMESTAMP TIMESTAMP O Spanner armazena o horário independentemente do fuso horário. Se você precisar armazenar um fuso horário, deverá usar uma coluna STRING separada. As funções SQL são fornecidas para converter carimbos de data/hora em uma string formatada, usando fusos horários.
CHAR, VARCHAR STRING Observação: o Spanner usa apenas strings Unicode.
O VARCHAR é compatível com um comprimento máximo de 65.535 bytes, enquanto o Spanner é compatível com até 2.621.440 caracteres.
BINARY, VARBINARY, BLOB, TINYBLOB BYTES Objetos pequenos (menos de 10 MiB) podem ser armazenados como BYTES. Considere usar ofertas alternativas do Google Cloud, como o Cloud Storage, para armazenar objetos maiores
TEXT, TINYTEXT, ENUM STRING Valores TEXT pequenos (menos de 10 MiB) podem ser armazenados como STRING. Considere usar ofertas alternativas do Google Cloud, como o Cloud Storage, para oferecer suporte a valores TEXT maiores.
ENUM STRING A validação dos valores ENUM precisa ser executada no aplicativo.
SET ARRAY<STRING> A validação dos valores do elemento SET precisa ser executada no aplicativo.
LONGBLOB, MEDIUMBLOB BYTES ou STRING contendo o URI para o objeto. Objetos pequenos (menos de 10 MiB) podem ser armazenados como BYTES. Considere usar ofertas alternativas do Google Cloud, como o Cloud Storage, para armazenar objetos maiores.
LONGTEXT, MEDIUMTEXT STRING (contendo dados ou o URI do objeto externo) Objetos pequenos (menos de 2.621.440 caracteres) podem ser armazenados como STRING. Considere usar ofertas alternativas do Google Cloud, como o Cloud Storage, para armazenar objetos maiores
JSON JSON Strings JSON pequenas (menos de 2.621.440 caracteres) podem ser armazenadas como JSON. Considere usar ofertas alternativas do Google Cloud, como o Cloud Storage, para armazenar objetos maiores.
GEOMETRY, POINT, LINESTRING, POLYGON, MULTIPOINT, MULTIPOLYGON, GEOMETRYCOLLECTION O Spanner não é compatível com tipos de dados geoespaciais. É preciso armazenar esses dados usando tipos de dados padrão e implementar alguma lógica de pesquisa/filtragem na camada do aplicativo.

Processo de migração

Este é um exemplo do cronograma geral do processo de migração:

  1. Conversão do esquema e do modelo de dados
  2. Conversão de consultas SQL.
  3. Migrar seu aplicativo para usar o Spanner além do MySQL.
  4. Exportar seus dados em massa do MySQL e importe-os para o Spanner usando o Dataflow.
  5. Manter a consistência entre os dois bancos de dados durante a migração.
  6. Migrar seu aplicativo para fora do MySQL.

Etapa 1: converter o banco de dados e o esquema

Converta o esquema atual em um esquema do Spanner para armazenar seus dados. Simplifique as modificações na aplicação. Verifique se o esquema convertido corresponde, da maneira mais próxima possível, ao esquema atual do MySQL. No entanto, devido às diferenças nos recursos, algumas alterações podem ser necessárias.

Usar as práticas recomendadas do design de esquemas ajudará você a aumentar a capacidade e reduzir os gargalos no banco de dados do Spanner.

Chaves primárias

No Spanner, toda tabela que precisa armazenar mais de uma linha precisa ter uma chave primária que consiste em uma ou mais colunas da tabela. A chave primária da sua tabela identifica exclusivamente cada linha em uma tabela, e o Spanner usa a chave primária para classificar as linhas. Como o Spanner é altamente distribuído, é importante escolher uma técnica de geração de chave primária que se ajuste bem ao crescimento dos dados. Para mais informações, consulte as estratégias de migração de chaves primárias recomendadas.

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.

Intercalar suas tabelas

O Spanner tem um recurso com que é possível definir duas tabelas como tendo um relacionamento pai e filho de um para muitos. Esse recurso intercala as linhas de dados filhas com as linhas pai no armazenamento, fazendo efetivamente a pré-junção da tabela e melhorando a eficiência da recuperação de dados quando pai e filhas são consultadas em conjunto.

É preciso que a chave primária da tabela filha comece com a(s) coluna(s) de chave primária da tabela pai. Da perspectiva da linha filha, a chave primária da linha mãe é chamada de chave estrangeira. Você pode definir até seis níveis de relacionamentos pai-filho.

Você pode definir ações acionadas na exclusão para tabelas filhas, determinando o que acontece quando a linha mãe é excluída: todas as linhas filhas são excluídas ou a exclusão da linha mãe é bloqueada enquanto existem linhas filhas.

Veja um exemplo da criação de uma tabela de álbuns intercalada com a tabela mãe de intérpretes definida anteriormente:

CREATE TABLE Albums (
  SingerId     INT64 NOT NULL,
  AlbumId      INT64 NOT NULL,
  AlbumTitle   STRING(MAX),
) PRIMARY KEY (SingerId, AlbumId)
INTERLEAVE IN PARENT (Singers)
ON DELETE CASCADE;

Criar índices secundários

Você também pode criar índices secundários para indexar dados da tabela fora da chave primária. O Spanner implementa índices secundários da mesma forma que as tabelas, então os valores das colunas que serão usados como chaves de índice terão as mesmas restrições das chaves primárias das tabelas. Isso também significa que os índices têm as mesmas garantias de consistência das tabelas do Spanner.

Pesquisas de valor por índices secundários são, na prática, iguais a consultas com uma mesclagem de tabela. Para melhorar o desempenho das consultas que usam índices, armazene cópias dos valores da coluna da tabela original no índice secundário usando a cláusula STORING, o que faz com que ele seja um índice de cobertura.

O otimizador de consultas do Spanner usa automaticamente um índice secundário somente quando o próprio índice armazena todas as colunas que estão sendo consultadas (uma consulta coberta). Para forçar o uso de um índice ao consultar colunas na tabela original, é necessário usar uma diretiva FORCE INDEX na instrução SQL. Por exemplo:

SELECT *
FROM MyTable@{FORCE_INDEX=MyTableIndex}
WHERE IndexedColumn=@value

É possível usar índices para aplicar valores exclusivos em uma coluna da tabela, basta definir um índice UNIQUE nessa coluna. O índice impedirá a inclusão de valores duplicados.

Aqui está um exemplo de instrução DDL em que é criado um índice secundário para a tabela "Álbuns":

CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle);

Se você criar outros índices depois que seus dados forem carregados, o preenchimento do índice poderá levar algum tempo. Recomendamos que você limite a taxa usada para adicionar esses índices a uma média de três por dia. Para mais orientações sobre a criação de índices secundários, consulte Índices secundários. Para mais informações sobre as limitações na criação de índice, consulte Atualizações de esquema.

Etapa 2: traduzir todas as consultas SQL

O Spanner usa o dialeto ANSI 2011 do SQL com extensões e tem muitas funções e operadores para converter e agregar seus dados. Todas as consultas SQL que usam o dialeto, as funções e os tipos específicos do MySQL precisarão ser convertidas para serem compatíveis com o Spanner.

Embora o Spanner não seja compatível com dados estruturados como definições de coluna, é possível usar dados estruturados em consultas SQL usando os tipos ARRAY<> e STRUCT<>. Por exemplo, escreva uma consulta que retorne todos os álbuns de um artista usando um ARRAY de STRUCTs (aproveitando os dados pré-mesclados). Para mais informações, consulte a seção Subconsultas da documentação.

É possível criar perfis de consultas SQL usando a interface de consultas do Spanner no console do Google Cloud para executá-las. Em geral, as consultas que executam varreduras de tabela completas em tabelas grandes são muito caras e devem ser usadas com moderação. Para mais informações sobre otimização de consultas SQL, consulte a documentação de práticas recomendadas de SQL.

Etapa 3: migrar seu aplicativo para usar o Spanner

O Spanner oferece um conjunto de bibliotecas de cliente para várias linguagens e a capacidade de ler e gravar dados usando chamadas de API específicas do Spanner, consultas SQL e instruções da linguagem de manipulação de dados (DML, na sigla em inglês). O uso de chamadas de API pode ser mais rápido para algumas consultas, como leituras diretas de linhas por chave, porque a instrução SQL não precisa ser traduzida.

Você tem a opção de usar o driver Java Database Connectivity (JDBC) para se conectar ao Spanner, aproveitando as ferramentas e a infraestrutura atuais que não têm integração nativa.

Como parte do processo de migração, os recursos não disponíveis no Spanner, conforme mencionado acima, precisam ser implementados no aplicativo. Por exemplo, um gatilho para verificar valores de dados e atualizar uma tabela relacionada precisaria ser implementado no aplicativo usando uma transação de leitura/gravação para ler a linha atual, verificar a restrição e gravar as linhas atualizadas em ambas as tabelas.

O Spanner oferece transações de leitura/gravação e somente leitura para garantir a consistência externa dos seus dados. Além disso, as transações de leitura podem ter os limites de carimbo de data/hora aplicados quando você está lendo uma versão consistente dos dados:

  • em um momento exato no passado (até 1 hora atrás);
  • no futuro (em que a leitura será bloqueada até que chegue a hora);
  • com uma quantidade aceitável de inatividade definida, que retornará uma visualização consistente com até algum tempo no passado, sem precisar verificar se os dados posteriores estão disponíveis em outra réplica. Isso pode aumentar o desempenho, mas pode retornar dados obsoletos.

Etapa 4: transferir seus dados do MySQL para o Spanner

Para transferir seus dados do MySQL para o Spanner, você precisa exportar seu banco de dados MySQL para um formato de arquivo portátil, por exemplo, XML e depois importar esses dados para o Spanner usando o Dataflow.

transferência de dados do MySQL para o Spanner

Exportação em massa do MySQL

A ferramenta mysqldump,incluída no MySQL, é capaz de exportar o banco de dados inteiro para arquivos XML bem formados. Como alternativa, use a instrução SQL SELECT ... INTO OUTFILE para criar arquivos CSV para cada tabela. No entanto, essa abordagem tem a desvantagem de que apenas uma tabela pode ser exportada por vez. O significado disso é que você precisa pausar seu aplicativo ou desativar seu banco de dados para que o banco de dados permaneça em um estado consistente para exportação.

Depois de exportar esses arquivos de dados, recomendamos que você os envie para um bucket do Cloud Storage para que possam ser importados.

Importação em massa para o Spanner

Como os esquemas de banco de dados provavelmente diferem entre o MySQL e o Spanner, talvez seja necessário tornar algumas conversões de dados parte do processo de importação. A maneira mais fácil de realizá-las e importar os dados para o Spanner é por meio do Dataflow. O Dataflow é o serviço distribuído do Google Cloud para extração, transformação e carregamento (ETL, na sigla em inglês). Ele oferece uma plataforma para executar canais de dados criados com o SDK do Apache Beam, além de ler e processar grandes quantidades de dados em paralelo em várias máquinas.

O SDK do Apache Beam exige a criação de um programa Java simples para definir a leitura, a transformação e a gravação dos dados. Existem conectores do Beam para Cloud Storage e Spanner, o único código que precisa ser escrito é a própria transformação dos dados.

Para ver um exemplo de um pipeline simples que lê arquivos CSV e grava no Spanner, consulte o repositório de código de amostra.

Se usar as tabelas mães-filhas intercaladas forem usadas no esquema do Spanner, você deverá tomar cuidado durante o processo de importação para que a linha mãe seja criada antes da linha filha. O código de canal de importação do Spanner gerencia isso importando primeiro todos os dados para as tabelas do nível raiz, depois todas as tabelas filhas do nível 1, depois todas as tabelas filhas do nível 2 e assim por diante.

Você pode usar o canal de importação do Spanner diretamente para importar seus dados em massa, mas essa abordagem exige que seus dados existam em arquivos Avro usando o esquema correto.

Etapa 5: manter a consistência entre os dois bancos de dados

Muitos aplicativos têm requisitos de disponibilidade que impossibilitam que fiquem off-line pelo tempo necessário para exportar e importar os dados. Portanto, enquanto você está transferindo seus dados para o Spanner, seu aplicativo continua a modificar o banco de dados atual. Por isso, é necessário duplicar as atualizações no banco de dados do Spanner enquanto o aplicativo estiver em execução.

Existem vários métodos para manter seus dois bancos de dados sincronizados, incluindo a captura de dados de alterações e a implementação de atualizações simultâneas no aplicativo.

Captura de dados de alterações

O MySQL não tem um utilitário nativo de captura de dados de alterações (CDC, na sigla em inglês). No entanto, há vários projetos de código aberto que podem receber binlogs do MySQL e convertê-los em um fluxo de CDC. Por exemplo, é possível conseguir um fluxo de CDC para seu banco de dados usando o daemon Maxwell.

Escreva um aplicativo que assine esse fluxo e aplique as mesmas modificações (após a conversão de dados, é claro) ao banco de dados do Spanner.

Atualizações simultâneas no aplicativo em ambos os bancos de dados

Um método alternativo seria o de modificar o aplicativo para executar gravações em ambos os bancos de dados. Um banco de dados (inicialmente o MySQL) é considerado a fonte da verdade e, após cada gravação do banco de dados, a linha inteira é lida, convertida e gravada no banco de dados do Spanner. Dessa forma, o aplicativo substitui constantemente as linhas do Spanner com os dados mais recentes.

Quando tiver certeza de que todos os seus dados foram transferidos corretamente, é possível mudar a lugar confiável para o banco de dados do Spanner. Com esse mecanismo, você tem um caminho de reversão se forem encontrados problemas ao mudar para o Spanner.

Verificar a consistência dos dados

À medida que os dados são transmitidos para o banco de dados do Spanner, é possível executar periodicamente uma comparação entre os dados do Spanner e os dados do MySQL para garantir que os dados sejam consistentes. Valide a consistência consultando as duas fontes de dados e comparando os resultados.

Use o Dataflow para realizar uma comparação detalhada de grandes conjuntos de dados usando a transformação de mesclagem. Essa transformação usa dois conjuntos de dados com chaves e corresponde os valores pelas chaves. Os valores correspondentes podem ser comparados por igualdade. É possível executar essa verificação regularmente, até que o nível de consistência corresponda aos requisitos do seu negócio.

Etapa 6: mudar para o Spanner como a fonte da verdade do seu aplicativo

Quando você confiar na migração de dados, poderá mudar seu aplicativo para usar o Spanner como lugar confiável. Se você continuar gravando alterações no banco de dados MySQL, isso manterá o banco de dados MySQL atualizado e oferecerá um caminho para reversão, caso surjam problemas.

Finalmente, é possível desativar e remover o código de atualização do banco de dados MySQL e encerrar o banco de dados MySQL, agora obsoleto.

Exportar e importar bancos de dados do Spanner

Como opção, é possível exportar as tabelas do Spanner para um bucket do Cloud Storage usando um modelo do Dataflow para realizar a exportação. A pasta resultante contém um conjunto de arquivos Avro e arquivos de manifesto JSON com suas tabelas exportadas. Esses arquivos podem servir a vários propósitos, incluindo:

  • Fazer o backup do banco de dados para conformidade com a política de retenção de dados ou recuperação de desastres.
  • importar o arquivo Avro para outras ofertas do Google Cloud, como o BigQuery.

Para mais informações sobre o processo de exportação e importação, consulte Como exportar bancos de dados e Como importar bancos de dados.

A seguir