Crie um índice em uma grande tabela de produção MySQL sem bloqueio de tabela

104

Eu preciso criar um índice em uma tabela MySQL de ~ 5 milhões de linhas. É uma tabela de produção, e temo um bloqueio completo de tudo se eu executar uma instrução CREATE INDEX ...

Existe uma maneira de criar esse índice sem bloquear inserções e seleções?

Só me perguntando, não tenho como parar, criar índice e reiniciar meu sistema!

noturno
fonte
1
certifique-se de que myisam_sort_buffer_size e myisam_max_sort_file_size são grandes o suficiente.
Jon Black

Respostas:

130

Atualização [2017]: MySQL 5.6 tem suporte para atualizações de índice online

https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html#online-ddl-index-syntax-notes

No MySQL 5.6 e superior, a tabela permanece disponível para operações de leitura e gravação enquanto o índice está sendo criado ou eliminado. A instrução CREATE INDEX ou DROP INDEX só termina depois que todas as transações que estão acessando a tabela são concluídas, para que o estado inicial do índice reflita o conteúdo mais recente da tabela. Anteriormente, modificar a tabela enquanto um índice estava sendo criado ou eliminado normalmente resultava em um conflito que cancelava a instrução INSERT, UPDATE ou DELETE na tabela.

[2015] Atualizando a tabela indica a escrita de blocos no MySQL 5.5

Da resposta acima:

"Se você estiver usando uma versão superior a 5.1 índices são criados enquanto o banco de dados está online. Portanto, não se preocupe, você não interromperá o uso do sistema de produção."

Isso é **** FALSE **** (pelo menos para tabelas MyISAM / InnoDB, que é o que 99,999% das pessoas usam. A Edição em cluster é diferente.)

Fazer operações UPDATE em uma tabela irá BLOQUEAR enquanto o índice está sendo criado. O MySQL é muito, muito estúpido sobre isso (e algumas outras coisas).

Script de teste:

(   
  for n in {1..50}; do
    #(time mysql -uroot -e 'select  * from website_development.users where id = 41225\G'>/dev/null) 2>&1 | grep real;
    (time mysql -uroot -e 'update website_development.users set bio="" where id = 41225\G'>/dev/null) 2>&1 | grep real;
  done
) | cat -n &
PID=$!
sleep 0.05
echo "Index Update - START"
mysql -uroot website_development -e 'alter table users add index ddopsonfu (last_name, email, first_name, confirmation_token, current_sign_in_ip);'
echo "Index Update - FINISH"
sleep 0.05
kill $PID
time mysql -uroot website_development -e 'drop index ddopsonfu on users;'

Meu servidor (InnoDB):

Server version: 5.5.25a Source distribution

Saída (observe como o sexto bloco de operação para os ~ 400ms leva para terminar a atualização do índice):

 1  real    0m0.009s
 2  real    0m0.009s
 3  real    0m0.009s
 4  real    0m0.012s
 5  real    0m0.009s
Index Update - START
Index Update - FINISH
 6  real    0m0.388s
 7  real    0m0.009s
 8  real    0m0.009s
 9  real    0m0.009s
10  real    0m0.009s
11  real    0m0.009s

Vs operações de leitura que não bloqueiam (troque o comentário de linha no script):

 1  real    0m0.010s
 2  real    0m0.009s
 3  real    0m0.009s
 4  real    0m0.010s
 5  real    0m0.009s
Index Update - START
 6  real    0m0.010s
 7  real    0m0.010s
 8  real    0m0.011s
 9  real    0m0.010s
...
41  real    0m0.009s
42  real    0m0.010s
43  real    0m0.009s
Index Update - FINISH
44  real    0m0.012s
45  real    0m0.009s
46  real    0m0.009s
47  real    0m0.010s
48  real    0m0.009s

Atualizar o esquema do MySQL sem tempo de inatividade

Portanto, há apenas um método que conheço para atualizar um esquema do MySql e não sofrer uma interrupção de disponibilidade. Mestres circulares:

  • Master A tem seu banco de dados MySQL em execução
  • Coloque o Mestre B em serviço e faça com que ele replique as gravações do Mestre A (B é um escravo de A)
  • Execute a atualização do esquema no Master B. Ele ficará para trás durante a atualização
  • Deixe o Mestre B alcançá-lo. Invariante: sua mudança de esquema DEVE ser capaz de processar comandos replicados de um esquema de downversion. As alterações de indexação qualificam. Geralmente, adições simples de colunas se qualificam. Removendo uma coluna? provavelmente não.
  • Troque ATOMICAMENTE todos os clientes do Mestre A para o Mestre B. Se quiser ficar seguro (confie em mim, você precisa), você deve garantir que a última gravação em A seja replicada para B ANTESB faz sua primeira gravação. Se você permitir gravações simultâneas em mais de 2 mestres, ... você entende melhor a replicação do MySQL em um nível DEEP ou está caminhando para um mundo de dor. Dor extrema. Tipo, você tem uma coluna que é AUTOINCREMENT ??? você está ferrado (a menos que use números pares em um mestre e probabilidades no outro). NÃO confie na replicação do MySQL para "fazer a coisa certa". NÃO é inteligente e não irá salvá-lo. É apenas um pouco menos seguro do que copiar logs de transações binárias da linha de comando e reproduzi-los manualmente. Ainda assim, desconectar todos os clientes do antigo mestre e transferi-los para o novo mestre pode ser feito em questão de segundos, muito mais rápido do que esperar por uma atualização de esquema de várias horas.
  • Agora Master B é seu novo mestre. Você tem o novo esquema. A vida é boa. Tomar uma cerveja; o pior já passou.
  • Repita o processo com o Mestre A, atualizando seu esquema para que ele se torne seu novo mestre secundário, pronto para assumir no caso de seu mestre primário (mestre B agora) perder força ou simplesmente morrer em você.

Uma maneira fácil de atualizar o esquema, não é. Viável em um ambiente de produção sério; Sim, ele é. Por favor, por favor, se houver uma maneira mais fácil de adicionar um índice a uma tabela MySQL sem bloquear gravações, me avise.

Googling me levou a este artigo que descreve uma técnica semelhante. Melhor ainda, aconselham beber no mesmo ponto do procedimento (observe que escrevi minha resposta antes de ler o artigo)!

Mudança de esquema pt-online de Percona

O artigo vinculado acima fala sobre uma ferramenta, pt-online-schema-change , que funciona da seguinte maneira:

  • Crie uma nova tabela com a mesma estrutura da original.
  • Atualize o esquema na nova tabela.
  • Adicione um gatilho na tabela original para que as alterações sejam mantidas em sincronia com a cópia
  • Copie linhas em lotes da tabela original.
  • Remova a mesa original do caminho e substitua por uma nova.
  • Largue a mesa velha.

Nunca experimentei a ferramenta sozinho. YMMV

RDS

Atualmente, estou usando o MySQL por meio do RDS da Amazon . É um serviço realmente bacana que finaliza e gerencia o MySQL, permitindo adicionar novas réplicas de leitura com um único botão e atualizar o banco de dados de forma transparente em todos os SKUs de hardware. É muito conveniente. Você não obtém acesso SUPER ao banco de dados, então não pode atrapalhar a replicação diretamente (isso é uma bênção ou uma maldição?). No entanto, você pode usar Read Replica Promotion para fazer suas alterações de esquema em um escravo somente leitura e, em seguida, promover esse escravo para se tornar seu novo mestre. Exatamente o mesmo truque que descrevi acima, apenas muito mais fácil de executar. Eles ainda não fazem muito para ajudá-lo com a transição. Você tem que reconfigurar e reiniciar seu aplicativo.

Dave Dopson
fonte
3
pt-online-schema-change funciona muito bem mesmo em uma replicação mestre-escravo. Eu o usei para fazer a migração ao vivo em uma tabela de registros de leitura ocupada de mais de 20 milhões em nosso banco de dados mestre de produção com 2 escravos de replicação sem qualquer soluço ou tempo de inatividade. Leva algum tempo para preparar o script, e geralmente tenho que criar um arquivo .sql contendo a mudança de SQL bruta e um arquivo .sh como wrapper para executar o mesmo SQL, mas em formato de fragmento (sem ALTER TABLE). Você pode executar vários comandos com pt-online-schema-change agrupando-os e separados por vírgula.
Alex Le
-1; Não sei sobre as versões mais antigas, mas sei que a criação de índice não bloqueia DML simultâneo no MySQL 5.6+ (para o qual existia um RC no momento em que esta resposta foi escrita, e que havia sido lançado oficialmente quando esta resposta foi finalizada editado em maio de 2013) porque confiei nisso para executar criações de índice de várias horas em tabelas de produção enquanto ainda aceito inserções. E embora você possa estar certo sobre o bloqueio da criação de índice DML em 5.5 e abaixo, o atraso de sub-segundo demonstrado aqui não é totalmente convincente.
Mark Amery de
@MarkAmery - bloqueio de comportamento é bloqueio de comportamento, e 400 ms é uma eternidade. Blocos do MySQL 5.5 para atualizações de índice. Crie um banco de dados de teste maior e ele será bloqueado por segundos, horas ou dias. Eu escrevi este post antes que o MySQL 5.6 tivesse atualizações de esquema online, então meu conteúdo original não reflete esse fato. Eu atualizei a postagem para refletir as novas informações disponíveis.
Dave Dopson
@DaveDopson, você tem 100% de certeza de que apenas as operações UPDATE estão bloqueadas?
toto_tico
Esse foi o caso da versão que testei.
Dave Dopson
67

Como esta postagem do blog descreve, o ALTER TABLEmecanismo InnoDB foi completamente redesenhado para o MySQL 5.6.

(Para uma visão geral exclusiva deste tópico, a documentação do MySQL pode fornecer uma tarde de leitura válida.)

Para adicionar um índice a uma tabela sem um bloqueio resultante em UPDATE/ INSERT, o seguinte formato de instrução pode ser usado:

ALTER TABLE my_table ADD INDEX my_table__idx (my_column), ALGORITHM=INPLACE, LOCK=NONE;
Desenhou
fonte
4
Advertência: dba.stackexchange.com/questions/138363/…
Alexander Torstling
16

Atualização do MySQL 5.6 (fev 2013): agora você pode executar operações de leitura e gravação enquanto um índice está sendo criado, mesmo com tabelas InnoDB - http://dev.mysql.com/doc/refman/5.6/en/innodb-create-index -overview.html

No MySQL 5.6 e superior, a tabela permanece disponível para operações de leitura e gravação enquanto o índice está sendo criado ou eliminado. A instrução CREATE INDEX ou DROP INDEX só termina depois que todas as transações que estão acessando a tabela são concluídas, para que o estado inicial do índice reflita o conteúdo mais recente da tabela. Anteriormente, modificar a tabela enquanto um índice estava sendo criado ou eliminado normalmente resultava em um conflito que cancelava a instrução INSERT, UPDATE ou DELETE na tabela.

e:

No MySQL 5.6, este recurso se torna mais geral: você pode ler e escrever em tabelas enquanto um índice está sendo criado, e muitos outros tipos de operações ALTER TABLE podem ser realizados sem copiar a tabela, sem bloquear operações DML, ou ambos. Portanto, no MySQL 5.6 e superior, normalmente nos referimos a este conjunto de recursos como DDL online em vez de Criação rápida de índice.

de http://dev.mysql.com/doc/refman/5.6/en/glossary.html#glos_fast_index_creation

Eric Saboia
fonte
Então, como a análise de Dave pode ser explicada?
Nikhil Sahu
1
@NikhilSahu Dave claramente não estava testando no MySQL 5.6, mas em uma versão mais antiga. Observe que a versão 5.6 ainda não foi lançada no ponto em que Dave postou a revisão inicial de sua resposta.
Mark Amery de
+1. Minha análise foi no MySQL 5.5 (o último disponível em 2013). Estou atualizando minha resposta para refletir os novos recursos do MySQL 5.6.
Dave Dopson
3

pt-online-schema-change é o caminho a percorrer se você realmente deseja ter certeza de que a migração não derrubará o site.

Como escrevi no comentário acima, tenho várias experiências com pt-online-schema-change em produção. Temos nossa tabela principal de mais de 20 milhões de registros e um mestre -> 2 escravos de replicação somente leitura. Fiz pelo menos uma dúzia de migrações com pt-online-schema-change desde a adição de uma nova coluna, alterando o conjunto de caracteres, até a adição de vários índices. Atendemos muito tráfego durante o período de migração também e não tivemos nenhum problema. É claro que você terá que testar todos os scripts completamente antes de executá-los na produção.

Tentei agrupar as alterações em 1 script de modo que pt-online-schema-change só precise copiar os dados uma vez. E tenha muito cuidado ao alterar o nome da coluna, pois você perderá seus dados. No entanto, adicionar um índice deve ser adequado.

Alex Le
fonte
Não concordo com sua recomendação irrestrita de pt-online-schema-change. É ótimo, mas é um exagero para muitas situações em que os recursos DDL online do MySQL 5.6 + já funcionam bem. Ele também tem limitações (como não brincar bem com triggers) e dobra a quantidade de escrita necessária por inserção na tabela original enquanto uma mudança de esquema está em andamento. Isso sobrecarregará seu disco significativamente mais do que uma alteração de esquema on-line comum e, portanto, tem o potencial de "derrubar seu site" em circunstâncias em que apenas executar a alteração de esquema de forma simples funcionaria bem.
Mark Amery de
Escrevi com base na minha experiência real com pt-online-schema-change na época, então não tenho certeza por que você chamaria minha recomendação de "não qualificada". Estávamos tendo pelo menos mais de 1000 visitantes no site a qualquer momento quando eu executei as alterações de esquema e, é claro, o IO de disco estava sobrecarregado, mas nosso site não caiu. Ter um bom armazenamento em cache também ajudou. Eu não usei MySQL 5.6+ DDL online, mas pela minha experiência, pt-online-schema-change fez um bom trabalho em nosso caso.
Alex Le
1
@AlexYe Caramba, eu quis dizer "não qualificado" no sentido de "sem reservas" em vez de "entregue por alguém que não está qualificado para comentar" - a última interpretação não me ocorreu até que eu vi seu comentário e certamente não é é o que eu pretendia! ou seja, eu estava dizendo que embora pt-online-schema-changeseja uma ferramenta útil, existem muitas situações em que o DDL online comum é tão bom e um punhado onde é melhor, então qualquer recomendação deve ser cuidadosamente advertida em vez de universal.
Mark Amery