Atualizando MySQL (Servidor Percona) de 5.7 para 8.0
O progresso não pára, então os motivos para atualizar para as versões mais recentes do MySQL estão se tornando cada vez mais convincentes. Não faz muito tempo, em um de nossos projetos, chegou a hora de atualizar os aconchegantes clusters Percona Server 5.7 para a versão 8. Tudo isso aconteceu na plataforma Ubuntu Linux 16.04. Como realizar tal operação com tempo de inatividade mínimo e quais problemas encontramos durante a atualização - leia neste artigo.
Treinamento
Qualquer atualização do servidor de banco de dados provavelmente está associada à reconfiguração do banco de dados: mudanças nos requisitos de limites de recursos do sistema e correção das configurações do banco de dados que precisam ser limpas de diretivas desatualizadas.
Antes de atualizar, iremos definitivamente consultar a documentação oficial:
Corrija os arquivos de configuração removendo diretivas desatualizadas.
Verifique a compatibilidade com utilitários.
Atualize os bancos de dados escravos instalando o pacote percona-server-server.
Atualize o master com o mesmo pacote.
Vamos analisar cada ponto do plano e ver o que pode dar errado.
IMPORTANTE! O procedimento de atualização de um cluster MySQL baseado em Galera possui sutilezas próprias que não são descritas no artigo. Você não deve usar esta instrução neste caso.
Parte 1: Verificando configurações
MySQL foi removido na versão 8 query_cache. Na verdade ele estava declarado obsoleto de volta na versão 5.7, mas agora completamente excluído. Por conseguinte, é necessário suprimir as directivas associadas. E para armazenar solicitações em cache agora você pode usar ferramentas externas - por exemplo, ProxySQL.
Também na configuração havia diretivas desatualizadas sobre innodb_file_format. Se no MySQL 5.7 foi possível selecionar o formato InnoDB, então a 8ª versão já funciona somente com formato Barracuda.
Nosso resultado é a remoção das seguintes diretivas:
query_cache_type, query_cache_limit и query_cache_size;
innodb_file_format и innodb_file_format_max.
Para verificar, usaremos a imagem Docker do Percona Server. Colocaremos a configuração do servidor no diretório mysql_config_test, e ao lado criaremos diretórios para dados e logs. Exemplo de teste de configuração do servidor Percona:
Resumindo: nos logs do Docker ou no diretório com os logs - dependendo de suas configurações - aparecerá um arquivo no qual as diretivas problemáticas serão descritas.
Aqui está o que tivemos:
2020-04-03T12:44:19.670831Z 0 [Warning] [MY-011068] [Server] The syntax 'expire-logs-days' is deprecated and will be removed in a future release. Please use binlog_expire_logs_seconds instead.
2020-04-03T12:44:19.671678Z 0 [Warning] [MY-013242] [Server] --character-set-server: 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous.
2020-04-03T12:44:19.671682Z 0 [Warning] [MY-013244] [Server] --collation-server: 'utf8_general_ci' is a collation of the deprecated character set UTF8MB3. Please consider using UTF8MB4 with an appropriate collation instead.
Assim, ainda precisávamos descobrir as codificações e substituir a diretiva desatualizada expire-logs-days.
Parte 2: Verificando instalações em funcionamento
A documentação de atualização contém 2 utilitários para verificar a compatibilidade do banco de dados. Seu uso ajuda o administrador a verificar a compatibilidade da estrutura de dados existente.
Vamos começar com o utilitário clássico mysqlcheck. Basta executar:
Se nenhum problema for encontrado, o utilitário será encerrado com o código 0:
Além disso, um utilitário está disponível nas versões modernas do MySQL shell mysql (no caso do Percona este é o pacote percona-mysql-shell). É um substituto para o cliente MySQL clássico e combina as funções de um cliente, um editor de código SQL e ferramentas de administração MySQL. Para verificar o servidor antes de atualizar, você pode executar os seguintes comandos através dele:
Em geral, nada crítico - apenas avisos sobre codificações (veja abaixo). Resultado geral da execução:
Decidimos que a atualização deveria ocorrer sem problemas.
Uma observação sobre os avisos acima indicando problemas com codificações. O fato é que UTF-8 no MySQL até recentemente não era "verdadeiro" UTF-8, já que armazenou apenas 3 bytes em vez de 4. No MySQL 8 isso é finalmente decidi consertar: alias utf8 em breve levará à codificação utf8mb4, e as colunas antigas nas tabelas se tornarão utf8mb3. Codificação adicional utf8mb3 será removido, mas não nesta versão. Portanto, decidimos corrigir as codificações já presentes na instalação do SGBD em execução, após atualizá-lo.
Parte 3: Atualizações do Servidor
O que poderia dar errado quando existe um plano tão inteligente? Compreendendo muito bem que nuances sempre acontecem, conduzimos o primeiro experimento em um cluster de desenvolvimento MySQL.
Como já mencionado, documentação oficial cobre a questão da atualização de servidores MySQL com réplicas. O resultado final é que você deve primeiro atualizar todas as réplicas (escravos), já que o MySQL 8 pode replicar a partir de uma versão master 5.7. Alguma dificuldade reside no fato de usarmos o modo mestre <-> mestre, quando o mestre remoto está no modo somente leitura. Ou seja, na verdade, o tráfego de combate vai para um data center e o segundo é de backup.
A topologia fica assim:
A atualização deve começar com réplicas réplica mysql dc 2, mestre mysql dc 2 и mysql replica dc 1e terminamos com o servidor mysql master dc 1. Para ser mais confiável, paramos as máquinas virtuais, tiramos snapshots delas e imediatamente antes da atualização paramos a replicação com o comando STOP SLAVE. O resto da atualização fica assim:
Reiniciamos cada réplica adicionando 3 opções às configurações: skip-networking, skip-slave-start, skip-log-bin. O fato é que a atualização do banco de dados gera logs binários com atualizações nas tabelas do sistema. Estas diretivas garantem que não haverá alterações nos dados da aplicação no banco de dados e que informações sobre atualização de tabelas do sistema não serão incluídas nos logs binários. Isso evitará problemas ao retomar a replicação.
Instalando o pacote percona-server-server. É importante notar que no MySQL versão 8 não você precisa executar o comando mysqlupgrade após a atualização do servidor.
Após um início bem-sucedido, reiniciamos o servidor novamente - sem os parâmetros que foram adicionados no primeiro parágrafo.
Garantimos que a replicação funcione com sucesso: verifique SHOW SLAVE STATUS e veja se as tabelas com contadores no banco de dados da aplicação estão atualizadas.
Tudo parece bastante simples: a atualização do desenvolvedor foi bem-sucedida. Ok, você pode agendar com segurança uma atualização noturna para produção.
Não houve tristeza - atualizamos o produto
No entanto, a transferência de experiência de desenvolvimento bem-sucedida para a produção não ocorreu sem surpresas.
Felizmente, o próprio processo de atualização começa com réplicas; portanto, quando encontramos dificuldades, interrompemos o trabalho e restauramos a réplica do instantâneo. A investigação dos problemas foi adiada para a manhã seguinte. Os logs continham as seguintes entradas:
2020-01-14T21:43:21.500563Z 2 [ERROR] [MY-012069] [InnoDB] table: t1 has 19 columns but InnoDB dictionary has 20 columns
2020-01-14T21:43:21.500722Z 2 [ERROR] [MY-010767] [Server] Error in fixing SE data for db1.t1
2020-01-14T21:43:24.208365Z 0 [ERROR] [MY-010022] [Server] Failed to Populate DD tables.
2020-01-14T21:43:24.208658Z 0 [ERROR] [MY-010119] [Server] Aborting
A pesquisa nos arquivos de diversas mailing lists do Google levou ao entendimento de que esse problema ocorre devido a Bug do MySQL. Embora seja mais provável que seja um bug de utilitário mysqlcheck и mysqlsh.
Acontece que o MySQL mudou a forma como eles representam os dados para campos decimais (int, tinyint, etc.), então o mysql-server usa uma maneira diferente de armazená-los. Se o seu banco de dados inicialmente estava na versão 5.5 ou 5.1 e depois atualizou para 5.7, talvez seja necessário OPTIMIZE para algumas mesas. Então o MySQL atualizará os arquivos de dados, transferindo-os para o formato de armazenamento atual.
Você também pode verificar isso com o utilitário mysqlfrm:
Se field_type Se for igual a 0, então o tipo antigo é usado na tabela - você precisa realizar OPTIMIZE. Porém, se o valor for 246, você já possui um novo tipo. Mais informações sobre os tipos podem ser encontradas em code.
Além disso, em esse bug Estamos considerando o segundo motivo possível que nos ultrapassou: a ausência de tabelas InnoDB na tabela do sistema INNODB_SYS_TABLESPACES, se elas, tabelas, foram criadas na versão 5.1. Para evitar problemas durante a atualização, você pode usar script SQL anexado.
Por que não tivemos esses problemas no desenvolvimento? O banco de dados é copiado periodicamente da produção - assim, tabelas são recriadas.
Infelizmente, em um grande banco de dados realmente funcional, você não será capaz de simplesmente pegar e executar um OPTIMIZE. percona-toolkit ajudará aqui: o utilitário pt-online-schema-change é excelente para a operação OPTIMIZE online.
O plano atualizado ficou assim:
Otimize todas as tabelas.
Atualize os bancos de dados.
Para verificar e ao mesmo tempo saber o horário de atualização, desabilitamos uma das réplicas e executamos o seguinte comando para todas as tabelas:
As tabelas são atualizadas sem bloqueios demorados porque o utilitário cria uma nova tabela temporária na qual copia os dados da tabela principal. No momento em que ambas as tabelas são idênticas, a tabela original é bloqueada e substituída pela nova. No nosso caso, um teste mostrou que levaria cerca de um dia para atualizar todas as tabelas, mas a cópia dos dados causou muita carga nos discos.
Para evitar isso, na produção adicionamos o argumento ao comando --sleep com valor 10 - este parâmetro ajusta o tempo de espera após a transferência de um lote de dados para uma nova tabela. Dessa forma, você pode reduzir a carga se o aplicativo em execução exigir tempo de resposta.
Após realizar a otimização, a atualização foi bem-sucedida.
... mas não completamente!
Meia hora após a atualização, o cliente apresentou um problema. O banco de dados funcionou de forma muito estranha: periodicamente eles iniciavam redefinições de conexão. Isto é o que parecia no monitoramento:
A captura de tela mostra um gráfico dente de serra devido ao fato de que alguns threads do servidor MySQL travavam periodicamente com um erro. Erros apareceram no aplicativo:
Uma rápida inspeção dos logs revelou que o daemon mysqld não conseguiu obter os recursos necessários do sistema operacional. Ao resolver os erros, descobrimos no sistema arquivos de política de apparmor "órfãos":
# dpkg -S /etc/apparmor.d/cache/usr.sbin.mysqld
dpkg-query: no path found matching pattern /etc/apparmor.d/cache/usr.sbin.mysqld
# dpkg -S /etc/apparmor.d/local/usr.sbin.mysqld
dpkg-query: no path found matching pattern /etc/apparmor.d/local/usr.sbin.mysqld
# dpkg -S /etc/apparmor.d/usr.sbin.mysqld
mysql-server-5.7: /etc/apparmor.d/usr.sbin.mysqld
# dpkg -l mysql-server-5.7
rc mysql-server-5.7 5.7.23-0ubuntu0.16.04.1 amd64
Esses arquivos foram criados durante a atualização para o MySQL 5.7 há alguns anos e pertencem a um pacote removido. Excluir os arquivos e reiniciar o serviço apparmor resolveu o problema:
Qualquer operação, mesmo a mais simples, pode levar a problemas inesperados. E mesmo ter um plano bem pensado nem sempre garante o resultado esperado. Agora, qualquer plano de atualização que nossa equipe tenha também inclui a limpeza obrigatória de arquivos desnecessários que possam ter surgido como resultado de ações recentes.
E com essa criatividade gráfica pouco profissional, gostaria de agradecer imensamente à Percona pelos excelentes produtos!