Qual é a maneira mais eficiente de agrupar consultas UPDATE no MySQL?

10

Estou escrevendo um aplicativo que precisa liberar um grande número de atualizações no banco de dados por um longo período de tempo e fiquei paralisado para otimizar a consulta. Atualmente estou usando INSERT INTO ... VALUES (..), (..) ON DUPLICATE KEY UPDATE, que funciona para agrupar todos os valores em uma consulta, mas executa incrivelmente lentamente em tabelas grandes. Na verdade, eu nunca preciso inserir linhas.

Outras abordagens que vi foram atualizar usando SET value = CASE WHEN...(o que seria difícil de gerar devido à maneira como estou criando as consultas e não tenho certeza sobre o desempenho de CASEcentenas / milhares de chaves) e simplesmente concatenadas múltiplas atualizações. Um desses seria mais rápido que o meu método atual?

Me surpreende que, até onde eu saiba, não haja uma maneira idiomática e eficiente de fazer isso no MySQL. Se realmente não há uma maneira mais rápida do que ON DUPLICATE KEY, valeria a pena mudar para o PostgreSQL e usar sua UPDATE FROMsintaxe?

Quaisquer outras sugestões também são muito apreciadas!

Editar: aqui está uma das tabelas que são atualizadas com frequência. Removi os nomes das colunas por serem irrelevantes.

CREATE TABLE IF NOT EXISTS `table` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `a` bigint(20) unsigned NOT NULL DEFAULT '0',
  `b` bigint(20) unsigned NOT NULL DEFAULT '0',
  `c` enum('0','1','2') NOT NULL DEFAULT '0',
  `d` char(32) NOT NULL,
  -- trimmed --
  PRIMARY KEY (`id`),
  KEY `a` (`a`),
  KEY `b` (`b`),
  KEY `c` (`c`),
  KEY `d` (`d`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;
jli
fonte
Isso está em uma máquina de teste e não na produção, portanto o InnoDB não está completamente ajustado adequadamente. Não tenho muita certeza de como o INSERT FROM opera, mas o que você disse parece certo. Atualizou a pergunta com as informações solicitadas.
JLI

Respostas:

14

Como você está usando InnoDBtabelas, a otimização mais óbvia seria agrupar vários UPDATEs em uma transação.

Com InnoDB, sendo um motor transacional, você não pagar apenas para o UPDATEpróprio, mas também para toda a sobrecarga transacional: gerir o buffer de transação, o log de transações, lavando o log para o disco.

Se você estiver logicamente confortável com a ideia, tente agrupar 100-1000 UPDATEs de cada vez, sempre agrupados da seguinte maneira:

START TRANSACTION;
UPDATE ...
UPDATE ...
UPDATE ...
UPDATE ...
COMMIT;

Possíveis desvantagens:

  • Um erro recolherá toda a transação (mas seria facilmente corrigido no código)
  • Você pode esperar um longo período de tempo para acumular seus 1000 UPDATEs, portanto, também pode querer ter um tempo limite
  • Mais complexidade no código do seu aplicativo.
Shlomi Noach
fonte