Eu tenho um servidor MySQL Percona 8, rodando via Docker, atuando como armazenamento de dados de apoio para um serviço muito utilizado. A cada hora, será executado um script que lê um valor de uma coluna virtual de aproximadamente 1,7 milhão de linhas (estimativa de tamanho da tabela de 2,3 GiB) e insere esse valor específico e os dados-chave associados em outra tabela da qual o sistema, de outra forma, apenas lê. A coluna virtual é uma pesquisa JSON json_extract(jsonData, '$.root.interestingValue')
e possui o GENERATED
sinalizador. A ideia por trás disso é colocar menos pressão sobre a tabela que é considerada ativa e atualizada regularmente, quando os usuários estão interessados apenas em alguns valores específicos e não é necessário recuperar o valor mais recente absoluto.
A consulta é a seguinte (com tabelas/colunas renomeadas)
CREATE TEMPORARY TABLE t1_cache_temp
SELECT
t2.id as uid,
t3.displayText as dt,
t2.virtualColumn as interestingValue
FROM liveTable t2
JOIN otherLiveTable t3 on t2.id = t3.id;
TRUNCATE TABLE t1_cache;
INSERT INTO t1_cache
SELECT uid, dt, interestingValue FROM t1_cache_temp;
DROP TEMPORARY TABLE t1_cache_temp;
O tempo total gasto por este script é de 47 segundos.
Embora a leitura da tabela InnoDB e a gravação na tabela temporária sejam adequadas, a gravação na outra tabela InnoDB faz com que todas as outras operações no banco de dados sejam interrompidas após os primeiros segundos. Eu reduzi a INSERT INTO t1_cache...
instrução executando cada instrução individualmente.
Também tentei escrever em tabelas novas e completamente não utilizadas (também usando InnoDB), o que dá o mesmo resultado. No momento da realização do último teste, apenas 2% do Máximo de Conexões permitidas estão em uso. Além disso, apenas 48% do buffer pool do InnoDB estava em uso.
Se eu mudar a mesa para MyISAM, tudo funciona normalmente, sem interrupções ou travamentos. Além disso, se eu remover a tabela temporária, o mesmo problema ocorrerá, independentemente do mecanismo InnoDB ou MyISAM.
Embora usar MyISAM seja provavelmente a melhor opção, o que poderia estar causando isso? Há algo que eu possa fazer sobre isso no nível da configuração?
Você disse que suas gravações são de 2,3 GiB, mas o tamanho do redo log do InnoDB é de 48 MiB. Estimo que uma dessas gravações preencheria o redo log mais de 49 vezes.
O redo log do InnoDB tem um tamanho fixo. As gravações não acrescentam nem aumentam, elas envolvem e sobrescrevem o arquivo de log. Cada vez que faz isso, o InnoDB deve pausar o SQL e liberar as páginas sujas do buffer pool para o disco até que uma boa parte do redo log não seja necessária. Este não será necessariamente o redo log completo , pode ser apenas o suficiente para o MySQL pensar que pode permitir mais gravações. Portanto, uma gravação de 2,3 GiB pode resultar em centenas de pontos de verificação.
Recomendo a leitura desta postagem do blog para obter uma boa explicação de como o tamanho do arquivo de log afeta a taxa de transferência: https://www.percona.com/blog/what-is-a-big-innodb_log_file_size/
O tamanho padrão do redo log do InnoDB é 48 MiB. Isto é suficiente apenas para um aplicativo com tráfego de gravação muito baixo. Por exemplo, um blog WordPress, que é em grande parte somente leitura e quando ocorrem gravações, elas são pequenas.
Se houver espaço de armazenamento suficiente, eu aumentaria o redo log para 4GiB se você quiser permitir uma taxa de transferência mais fácil de gravações ocasionais de 2,3GiB.
É uma vantagem dimensionar o arquivo de log com uma margem confortável, porque um ponto de verificação é acionado quando o log está 3/4 cheio. Consulte https://www.percona.com/blog/2011/04/04/innodb-flushing-theory-and-solutions/ para obter detalhes sobre isso.
PS Eu não uso MyISAM se puder evitá-lo. Veja minha resposta aqui por um motivo: https://stackoverflow.com/questions/20148/myisam-versus-innodb/17706717#17706717
Esse script está fazendo o dobro do trabalho necessário. Mudar para