0

Mon cluster MySQL: Ver 5.6.30-76.3-56 for debian-linux-gnu on x86_64 (Percona XtraDB Cluster (GPL), Release rel76.3, Revision aa929cb, WSREP version 25.16, wsrep_25.16)INSERT INTO SELECT prend beaucoup de temps sur le cluster

J'ai une requête SQL compliquée qui insère environ 36k lignes dans une table avec cette syntaxe:

INSERT INTO `sometable` (SELECT ...); 

La sélection est un peu compliqué mais pas lent (0.0023s) mais l'insertion prend environ 40-50s. La table n'est pas utilisée lorsque j'insère les lignes.

Mes questions sont les suivantes:

  • Puis-je accélérer en quelque sorte?
  • L'insertion lente provoque des problèmes de verrouillage sur les autres tables (à cause de select)
  • Ce workflow est une bonne ou une mauvaise pratique? Est-ce qu'il y a mieux?

Merci

MISE À JOUR:

Le schéma de la table:

CREATE TABLE `sometable` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `user_id` int(11) unsigned DEFAULT NULL, 
    `a` varchar(255) DEFAULT NULL, 
    `b` smallint(6) unsigned DEFAULT NULL, 
    `c` smallint(6) unsigned DEFAULT NULL, 
    `d` smallint(6) unsigned DEFAULT NULL, 
    `e` smallint(6) unsigned DEFAULT NULL, 
    `f` varchar(255) DEFAULT '', 
    `country_id` int(10) unsigned DEFAULT NULL, 
    `city_id` int(10) unsigned DEFAULT NULL, 
    `g` smallint(6) unsigned DEFAULT NULL, 
    `h` smallint(6) unsigned DEFAULT NULL, 
    `i` smallint(6) unsigned DEFAULT NULL, 
    `j` smallint(6) unsigned DEFAULT NULL, 
    `k` smallint(6) unsigned DEFAULT NULL, 
    `l` varchar(3) DEFAULT NULL, 
    `m` varchar(3) DEFAULT NULL, 
    `n` text, 
    `o` varchar(255) DEFAULT NULL, 
    `p` varchar(32) DEFAULT NULL, 
    `q` varchar(32) DEFAULT NULL, 
    `r` varchar(32) DEFAULT NULL, 
    `s` time DEFAULT NULL, 
    `t` time DEFAULT NULL, 
    `u` text, 
    PRIMARY KEY (`id`), 
    KEY `user_id` (`user_id`), 
    KEY `country_id` (`country_id`), 
    KEY `city_id` (`city_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

MAJ2:

Lorsque je tente d'exécuter la requête je reçois une erreur certains cas:

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction 

MA SOLUTION:

Voici ma solution finale si quelqu'un intéressé par: gist

Le principal problème est que si je remplis mytable les autres requêtes sont bloquées et le cluster a sérieux problèmes de performance. Dans cette solution, je crée une table temporaire et la remplis avec des données en mode "dirty sale", puis je copie ces données en mytable en morceaux, donc cela prend un peu plus de temps mais il n'y a pas de problèmes de performance et pas les requêtes.

+1

esprit partageant avec nous l'instruction 'CREATE TABLE' de votre table? (Inclure uniquement les éléments importants, tels que les clés, les colonnes indexées, les index, etc.) –

+1

Veuillez lire ce document http://meta.stackoverflow.com/a/271056/ en particulier la partie sur les performances des requêtes. Alors s'il vous plaît [modifier] votre question. –

+0

Etes-vous sûr que votre opération 'SELECT' est vraiment rapide? Lisez ceci: http://meta.stackoverflow.com/a/271056/ –

Répondre

1

Une opération qui renvoie une ligne de la longueur que vous décrivez toutes les 64 nanosecondes est très rapide. C'est ce que 36 kilowatts en 2,3 millisecondes. Il semble probable que votre timing de requête SELECT ne tient pas compte du transport de l'ensemble de résultats vers le client MySQL. Quoi qu'il en soit, utiliser cette performance comme une comparaison à une opération INSERT place vos attentes démesurément élevées.

Vous pouvez essayer d'exécuter cette commande avant de commencer votre opération. Il permettra à votre opération SELECT de procéder à moins de conflits avec le trafic de votre application sur les tables source pour le SELECT. Voir ici https://dev.mysql.com/doc/refman/5.7/en/set-transaction.html

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 

Vous pouvez essayer un processus en deux étapes, impliquant une table temporaire. Cela aura l'avantage de ne pas avoir à mettre à jour tous les index en some_table en même temps que l'opération SELECT. Cette opération ressemblera à ceci.

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 
CREATE TEMPORARY TABLE insert_batch AS SELECT ... ; 
INSERT INTO some_table SELECT * FROM insert_batch; 
DROP TEMPORARY TABLE insert_batch; 

Vous devez comprendre qu'InnoDB publie votre lot d'insertions sur votre table en une seule transaction. Si vous pouvez le faire d'une manière qui gère environ 500 lignes à la fois plutôt que 36K, vous aurez plus de transactions, mais elles seront plus petites. C'est généralement un moyen d'obtenir un débit plus élevé.

+0

Merci, cela m'a beaucoup aidé. Sur cette base, j'ai résolu le problème. Ce n'est pas si rapide mais il n'y a pas de problème de performance lors de l'exécution de la procédure. – MrRP

+0

De rien. Si vous avez le temps, pouvez-vous laisser un commentaire disant ce que vous avez fait et combien cela a amélioré la performance? –

+0

J'ai mis à jour la question avec ma solution. Si vous avez des commentaires à ce sujet, je l'apprécierais vraiment. – MrRP

1

Si tout le reste échoue, cela peut être une solution viable. Tout d'abord, voir http://mysql.rjweb.org/doc.php/deletebig#deleting_in_chunks

  1. Chargez vos corrections dans une table temporaire (ou une table MyISAM non répliquée).
  2. Parcourez la table temporaire (en utilisant un code similaire à ce lien). Choisissez 100 rangées à la fois.
  3. Effectuez le INSERT ... SELECT ... de 100 lignes dans une transaction séparée.

Cette technique peut prendre (ou non) plus de 40-50 secondes, mais au moins est beaucoup moins susceptible de délai d'attente ou d'interblocage.

En général, évitez d'exécuter une transaction qui dure plus de quelques secondes. Ce lien est un peu générique sur la façon de «tronquer» les opérations longues (et répétitives) pour éviter les longues transactions.

+0

Merci, j'ai utilisé l'idée des morceaux finalement. – MrRP