MySQL: a execução não pode ser perdoada





O site e loja online “Eldorado” faz cerca de 40 mil compras todos os dias. Provavelmente, não há necessidade de explicar o que isso significa para os negócios da empresa.



Historicamente, a loja funciona no mecanismo Bitrix com uma grande quantidade de código personalizado e add-ons. O armazenamento é um cluster MySQL com quatro servidores principais.



Um número significativo de empresas tem aplicativos monolíticos e muitos precisam trabalhar com eles. Existem muitas maneiras de lidar com o monólito, mas, infelizmente, poucas pessoas escrevem sobre os monólitos bem-sucedidos. Espero que a história sobre como sustentamos nosso monólito (até que o vimos) seja do seu interesse.



Estamos bem cientes de que a arquitetura massiva pode trazer muitos problemas. Mas é fácil destruí-lo, por uma simples decisão obstinada, é impossível: as vendas vão, o site deve funcionar, as mudanças para os usuários não devem ser radicais. Portanto, a transição de um monólito para um conjunto de microsserviços leva tempo, que precisamos resistir: para garantir que o sistema esteja operacional e sua resistência ao estresse.



Qual era o problema



Por muito tempo, o cluster de banco de dados no site eldorado.ru foi construído de acordo com o seguinte esquema:





Todos os mestres neste esquema trabalham simultaneamente e todos estão no modo ativo, reproduzindo sequencialmente o fluxo de replicação ... M1-> M2- > M3-> M4-> M1 -> M2-> M3-> M4-> M1-> M2 ...



Em nossa configuração, esta configuração deu a única vantagem - permitiu que o sistema funcionasse e mantivesse sua carga. O fato é que o balanceador de solicitação do aplicativo, após qualquer atualização para garantir a consistência, muda todo o fluxo de leitura para este mestre, e um mestre antes não era suficiente para conter todo o fluxo de leitura.



Mas tal esquema não poderia fornecer confiabilidade ou velocidade de trabalho. Embora parecesse simples, tinha várias falhas. Ela demorou muito para atualizar os dados no cluster: na pior das hipóteses, havia até cinco braços de replicação (dependendo de qual mestre as alterações foram iniciadas inicialmente). Devido a estes atrasos, surgiram muitos problemas tanto no funcionamento do site como na realização de encomendas na loja online.



Contras deste esquema :



  • Os escravos da zona mais distante do mestre ativo recebem atualizações de dados no pior caso somente após 4 vezes o tempo de execução da transação; às vezes, ocorriam atrasos frenéticos na replicação;
  • Qualquer falha em qualquer um dos mestres leva à inconsistência de dados em todo o cluster até que seja eliminado;
  • (- — );
  • ;
  • , ( , , );
  • UPDATE/DELETE SELECT ;
  • , slave_status seconds_behind_master.


Você pode emular esse comportamento em seus ambientes de teste habilitando um atraso de replicação artificial de 1-2 segundos nos escravos (o que fizemos). Esta é uma ótima maneira de testar seu aplicativo quanto à prontidão para tais arquiteturas distribuídas por meio da opção MASTER_DELAY = N .



e, finalmente, a transição para outro banco de dados no nosso caso não é uma opção, porque o sistema é demasiado grande escala e muito nele está ligada ao uso do MySQL apresenta e até mesmo para as nuances de seu otimizador de consulta interna.



Como resolvemos isso



Não queríamos mudar o esquema nós mesmos (esta é uma operação arriscada) e começamos por procurar uma empresa de consultoria que pudesse propor e implantar uma nova arquitetura, e fazê-lo para que o site ficasse acessível e a mudança não fosse perceptível. Entre essas empresas estavam os maiores integradores e desenvolvedores de software.



Algumas empresas simplesmente não nos responderam (e isso é normal), enquanto outras escreveram que não estavam prontas para assumir tal tarefa. Ao mesmo tempo, nem sequer se colocava a questão do possível custo do projeto.

Se os grandes integradores não querem se envolver em uma tarefa, torna-se duplamente interessante resolvê-la sozinhos. Tínhamos um bom equipamento à nossa disposição, os problemas de estabilidade e tolerância a falhas eram a segunda prioridade, e no início queríamos acelerar de alguma forma a transferência de dados. Várias opções que apareceram nas versões 5.6 e 5.7 do MySQL funcionaram bem para isso.



É verdade que a documentação sugeria de forma transparente que não seria possível simplesmente ativá-los, tk. no ringue definitivamente haverá um escravo com uma versão menor, mas aqui é assim:

O mestre 5.7 é capaz de ler os logs binários antigos escritos antes da atualização e enviá-los aos escravos 5.7. Os escravos reconhecem o formato antigo e o manipulam de maneira adequada.



Os logs binários criados pelo mestre após a atualização estão no formato 5.7. Eles também são reconhecidos pelos 5,7 escravos.



Em outras palavras, ao atualizar para o MySQL 5.7, os escravos devem ser MySQL 5.7 antes que você possa atualizar o mestre para 5.7.


Para os testes, foi o suficiente para levantarmos o anel de teste, por exemplo, através de mysqld_multi, e executar consultas típicas nele (você pode até mesmo no mesmo host, 4 instâncias em portas diferentes com conjuntos de deslocamento diferentes), algo assim:



mysql -h127.0.0.1 -P 3302 -e "RESET SLAVE; RESET MASTER; SHOW MASTER STATUS\G;"
mysql -h127.0.0.1 -P 3302 -e "CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=3301, MASTER_USER='root', MASTER_PASSWORD='', MASTER_LOG_FILE='master1-binlog.000001', MASTER_LOG_POS = 107;START SLAVE;"
mysql -h127.0.0.1 -P 3303 -e "RESET SLAVE; RESET MASTER; SHOW MASTER STATUS\G;"
mysql -h127.0.0.1 -P 3303 -e "CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=3302, MASTER_USER='root', MASTER_PASSWORD='', MASTER_LOG_FILE='master2-binlog.000001', MASTER_LOG_POS = 107;START SLAVE;"
mysql -h127.0.0.1 -P 3304 -e "RESET SLAVE; RESET MASTER; SHOW MASTER STATUS\G;"
mysql -h127.0.0.1 -P 3304 -e "CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=3303, MASTER_USER='root', MASTER_PASSWORD='', MASTER_LOG_FILE='master3-binlog.000001', MASTER_LOG_POS = 107;START SLAVE;"
mysql -h127.0.0.1 -P 3301 -e "RESET SLAVE; RESET MASTER; SHOW MASTER STATUS\G;"
mysql -h127.0.0.1 -P 3301 -e "CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=3304, MASTER_USER='root', MASTER_PASSWORD='', MASTER_LOG_FILE='master4-binlog.000001', MASTER_LOG_POS = 107;START SLAVE;"
      
      





Depois disso, você pode alterar a versão de qualquer instância executando o config com a porta desejada com outro binário, que foi colocado ao lado dele, e fazendo mysql_upgrade para as tabelas de dados / sistema.



Na venda, poderíamos, por um tempo limitado, enviar todo o tráfego para apenas um mestre, atualizar outro neste momento, em seguida, alternar para ele e atualizar novamente todos os outros. Mas, para isso, era necessário garantir a compatibilidade do formato binlog entre as versões, para que absolutamente todas as transações fossem perdidas com sucesso.



Um pouco mais de documentação útil para nosso caso:

Para evitar incompatibilidades, defina as seguintes variáveis ​​no mestre MySQL 5.6:



binlog_checksum=NONE
binlog_row_image=FULL
binlog_rows_query_log_events=OFF
log_bin_use_v1_row_events=1 (NDB Cluster only) 
      
      





Esta flag acabou sendo obrigatória para nós, embora não usemos nenhum NDB, sem ela, a replicação terminou entre os servidores 5.6 - 5.5, e mysqlbinlog lê o log sem esta opção com o erro ERROR: Error in Log_event :: read_log_event () : 'Verificação de integridade falhou', data_len: 8203, event_type: 30, se habilitado, então tudo começa e funciona.

Não incluímos o GTID, porque além da exigência de compatibilidade com todas as ferramentas antigas, objetivamente não vemos vantagens suficientes para a transição.



gtid_mode=OFF
      
      





O teste mais simples para verificar a exatidão da replicação é fazer o upload de um dump, por sua vez, para o servidor com 5.5 e o servidor com 5.6 e ver se tudo ficará bem.



Infelizmente, embora fosse de se esperar, os testes não tiveram sucesso.



Last_Error: Column 18 of table 'eldorado2.b_adv_banner' cannot be converted from type '<unknown type>' to type 'datetime'
      
      





datetime em 5.6 é especial, microssegundos são adicionados a ele, então em 5.6 ele tem um novo datetime, desconhecido na



versão 5.5 5.6 - ele pode funcionar em um cluster em um anel em paralelo com 5.5, se ao mesmo tempo nenhum campo for criado em qualquer uma das tabelas que passam por replicação com novos tipos de campos. (datetime 5.6! = ​​datetime 5.5, semelhante a time, timestamp, há mais de 240 desses campos em nosso banco de dados).



Não podíamos garantir totalmente a ausência de DDL com esses campos e não queríamos comprometer o desempenho de todo o cluster. Mas tínhamos um Plano B. mais seguro



Significava a presença de hardware adicional para manobras e levantamento de uma cópia completa do cluster próximo, felizmente, tínhamos esse hardware. E uma vez que existe essa possibilidade, então foi necessário criar um cluster "normal" de uma vez.



Mas, ao mesmo tempo, é necessário garantir a preservação da operabilidade de todas as ferramentas atuais de monitoramento, depuração e análise de binlog e eliminar, tanto quanto possível, todas as deficiências existentes na arquitetura atual.



Replicação multicanal



Uma bala de prata é necessária para manter os sites intactos e os administradores a serem alimentados. Esta é a replicação multicanal. A priori não confiávamos em novas oportunidades e não tínhamos certeza sobre a tecnologia, não podíamos encontrar tais informações ou casos em qualquer lugar, há pouca experiência pública em grandes produções.



Portanto, pensamos em tudo nós mesmos, o plano era o seguinte:



  • : 5.7, ;
  • ;
  • , — , , .








— , , , . , , ? . , « »! , - !

( « »)


No esquema de destino, 4 mestres são 4 fluxos de gravação independentes para cada escravo, que são processados ​​independentemente.



Em todos os mestres, agora era possível desligar o log_slave_updates - eles simplesmente não precisam retransmitir nada em qualquer lugar, eles enviam todas as alterações no fluxo principal (=> a carga no mestre é ainda menor).



E, ao mesmo tempo, você também pode ativar o formato binlog mínimo e o processamento paralelo de transações ao longo do caminho (de forma bastante condicional, você precisa entendê-lo corretamente):



slave_parallel_workers=5
slave_parallel_type=LOGICAL_CLOCK
binlog_row_image=minimal
      
      





Com essa configuração, poderíamos mudar a carga para um novo cluster a qualquer momento, mas essa rota é unilateral e não oferece reversão.



Para o tempo de vida das conexões com o cluster antigo, a opção log_slave_updates em um ponto de entrada mestre para o novo cluster ainda está lá e, portanto, todas as mudanças das conexões para o cluster "antigo" são perfeitamente entregues ao novo, e imediatamente após eles morrem esta opção foi desabilitada, o aplicativo até o momento olhou para 3 outros mestres e os fluxos de dados não se cruzaram de forma alguma.



Como resultado, obtivemos o seguinte conjunto de vantagens:



  • Se uma solicitação longa estiver bloqueando algo, isso afetará apenas um dos 4 threads de replicação e não afetará os outros de forma alguma;
  • O novo formato binlog, que antes era impossível apenas por causa da versão do MySQL, agora ocupa várias vezes menos espaço e, consequentemente, tráfego, devido a isso, muito mais alterações podem passar pelo cluster;
  • Agora você pode desligar qualquer mestre absolutamente sem dor, sem afetar todo o resto;
  • Os acidentes dos mestres agora não são tão assustadores, agora você pode clonar qualquer servidor em um minuto em qualquer situação incompreensível, recriar o server_id, criar créditos para acesso do escravo e - o novo mestre está pronto.


Também há um "menos":



  • Cada mestre tem muito mais escravos e é mais fácil entrar no canal (na verdade, isso não é um aumento no tráfego, mas uma redistribuição no tempo e no espaço).


O que o novo esquema deu



A mudança para o novo esquema deu certo, fizemos em um dia, 28 de agosto de 2020. A experiência de usar a nova arquitetura mostrou que o número de problemas de replicação foi reduzido de três a quatro vezes (é impossível se livrar deles completamente). A estabilidade do sistema aumentou. E o principal resultado foi um aumento na capacidade máxima do sistema. Se os desenvolvedores anteriores podiam culpar a replicação por quaisquer problemas incompreensíveis, agora isso não funciona para eles.



O número de problemas de cliente causados ​​por atrasos de replicação foi reduzido várias vezes, o que significa que os clientes estão experimentando pelo menos um pouco menos de dor. Agora podemos desligar qualquer servidor mestre a qualquer momento para realizar o trabalho nele. Isso não afeta todo o cluster nem interrompe o processo de replicação.



O cluster atende o site principal "Eldorado" - em sua maior parte, um antigo aplicativo monolítico com cartões de produto, uma conta pessoal, uma cesta, processamento de pedidos, um call center, etc. No momento da redação deste artigo, a carga total de leitura no cluster (apenas nos escravos) é de 40k rps, aproximadamente 5k rps por servidor de banco de dados, excluindo a carga técnica em escravos técnicos individuais, que é significativamente maior nos horários de pico. Pode não parecer muito, mas é preciso levar em consideração a natureza e a complexidade dessas consultas.



Nós realmente esperamos que nossa experiência seja útil para alguém. Além da replicação multicanal, também usamos muitas coisas interessantes, como blackhole e tabelas federadas, eles também permitem que você remova muitas dores de cabeça (e adicione um pouco para aqueles que não entendem porque eles são necessários), se alguém for interessado nas nuances e quaisquer outras dúvidas sobre o nosso MySQL - Bem-vindo nos comentários.



Por meio ano de operação comercial, ainda não encontramos nenhum problema relacionado a multicanal, e podemos definitivamente recomendar tal configuração como suficientemente tolerante a falhas e confiável.



O próprio monólito está agora em processo de corte em uma série de serviços separados e independentes, alguns dos quais iremos fragmentar e contar a você sobre nossa experiência - fique ligado.



Agradecimentos especiais à minha excelente equipe, não teríamos feito isso sem ela!



PS A propósito, ainda precisamos de programadores talentosos . Se você é assim, venha , vai ser interessante.



All Articles