web-dev-qa-db-fra.com

Quelle est la manière la plus efficace de grouper des requêtes UPDATE dans MySQL?

J'écris une application qui doit éliminer un grand nombre de mises à jour de la base de données pendant une période prolongée, et je suis resté coincé sur la façon d'optimiser la requête. Actuellement, j'utilise INSERT INTO ... VALUES (..), (..) ON DUPLICATE KEY UPDATE, qui fonctionne pour regrouper toutes les valeurs en une seule requête, mais s'exécute de manière atroce sur les grandes tables. Je n'ai jamais vraiment besoin d'insérer de lignes.

D'autres approches que j'ai vues consistent à mettre à jour à l'aide de SET value = CASE WHEN... (qui serait difficile à générer en raison de la façon dont je construis les requêtes, et je ne suis pas sûr des performances de CASE pour des centaines/milliers de clés), et simplement de multiples mises à jour concaténées. L'un ou l'autre serait-il plus rapide que ma méthode actuelle?

Cela me déconcerte que, pour autant que je sache, il n'y a pas de moyen idiomatique et efficace de le faire dans MySQL. S'il n'y a vraiment pas de moyen plus rapide que ON DUPLICATE KEY, vaudrait-il la peine de passer à PostgreSQL et d'utiliser son UPDATE FROM syntaxe?

Toutes autres suggestions sont également grandement appréciées!

Edit: voici l'un des tableaux qui est mis à jour fréquemment. J'ai supprimé les noms de colonnes car ils n'étaient pas pertinents.

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;
10
jli

Puisque vous utilisez des tables InnoDB, l'optimisation la plus évidente serait de regrouper plusieurs UPDATE dans une transaction.

Avec InnoDB, étant un moteur transactionnel, vous payez non seulement pour le UPDATE lui-même, mais aussi pour tous les frais généraux transactionnels: gestion du tampon de transaction, journal des transactions, vidage du journal sur le disque.

Si vous êtes logiquement à l'aise avec l'idée, essayez de regrouper 100-1000 UPDATEs à la fois, à chaque fois comme ceci:

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

Inconvénients possibles:

  • Une erreur réduira la transaction entière (mais serait facilement corrigée dans le code)
  • Vous pourriez attendre longtemps pour accumuler vos 1000 UPDATEs, donc vous pourriez aussi vouloir avoir un certain délai
  • Plus de complexité sur votre code d'application.
14
Shlomi Noach