web-dev-qa-db-fra.com

Lequel est le plus rapide: plusieurs INSERT ou un INSERT à plusieurs lignes?

J'essaie d'optimiser une partie de mon code qui insère des données dans MySQL. Devrais-je enchaîner des INSERT pour créer un énorme INSERT à plusieurs lignes ou est-ce que plusieurs INSERT séparés sont plus rapides?

173
dusoft

https://dev.mysql.com/doc/refman/8.0/en/insert-optimization.html

Le temps requis pour insérer une ligne est déterminé par les facteurs suivants, les chiffres indiquant des proportions approximatives:

  • Connexion: (3)
  • Envoi de requête au serveur: (2)
  • Requête d'analyse: (2)
  • Insérer une ligne: (1 × taille de la ligne)
  • Insertion d'index: (1 × nombre d'index)
  • Fermeture: (1)

Il devrait donc être évident qu’en envoyant une seule déclaration volumineuse, vous éviterez une surcharge de 7 déclarations par insertion, ce qui, à la lecture du texte, indique également:

Si vous insérez plusieurs lignes du même client en même temps, utilisez des instructions INSERT avec plusieurs listes VALUES pour insérer plusieurs lignes à la fois. Cela est considérablement plus rapide (plusieurs fois plus rapide dans certains cas) que d'utiliser des instructions INSERT distinctes à une seule ligne.

262
mbarkhau

Je sais que je réponds à cette question presque deux ans et demi après sa demande, mais je voulais simplement fournir des données concrètes d'un projet sur lequel je travaille en ce moment et qui montrent que la réalisation de plusieurs blocs VALUE par insertion est - BEAUCOUP plus rapide que les instructions INSERT séquentielles à bloc unique VALUE.

Le code que j'ai écrit pour ce repère en C # utilise ODBC pour lire les données en mémoire à partir d'une source de données MSSQL (environ 19 000 lignes, toutes sont lues avant le début de l'écriture) et le connecteur MySql .NET. (Mysql.Data. *) Pour INSERTER les données de la mémoire dans une table sur un serveur MySQL via des instructions préparées.Elle a été écrite de manière à me permettre d’ajuster dynamiquement le nombre de blocs VALUE par INSERT préparé (c.-à-d., insérer n lignes à la fois, où je pourrais ajuster la valeur de n avant une exécution.) J'ai également exécuté le test plusieurs fois pour chaque n.

L'exécution de blocs VALUE uniques (par exemple, une ligne à la fois) a pris 5,7 à 5,9 secondes. Les autres valeurs sont les suivantes:

2 rangées à la fois: 3,5 à 3,5 secondes
5 lignes à la fois: 2,2 à 2,2 secondes
10 lignes à la fois: 1,7 à 1,7 secondes
50 lignes à la fois: 1,17 - 1,18 secondes
100 lignes à la fois: 1,1 à 1,4 seconde
500 lignes à la fois: 1,1 à 1,2 seconde
1000 lignes à la fois: 1,17 - 1,17 secondes

Donc oui, même le simple regroupement de 2 ou 3 écritures procure une amélioration spectaculaire de la vitesse (temps d'exécution réduit d'un facteur n), jusqu'à atteindre un niveau compris entre n = 5 et n = 10, point auquel l'amélioration diminue nettement, et quelque part dans la gamme n = 10 à n = 50, l’amélioration devient négligeable.

J'espère que cela aidera les gens à décider (a) d'utiliser ou non l'idée de préparation multiple et (b) le nombre de blocs VALUE à créer par instruction (en supposant que vous souhaitiez utiliser des données suffisamment volumineuses pour pousser la requête au-delà de la taille maximale. pour MySQL, qui, à mon avis, fait 16 Mo par défaut dans de nombreux endroits, éventuellement plus ou moins grands en fonction de la valeur de max_allowed_packet définie sur le serveur.)

145
Jon Kloske

Un facteur important sera de savoir si vous utilisez un moteur transactionnel et si vous avez l'auto-validation activée.

Autocommit est activé par défaut et vous souhaitez probablement le laisser activé. par conséquent, chaque insertion que vous faites effectue sa propre transaction. Cela signifie que si vous faites une insertion par ligne, vous allez commettre une transaction pour chaque ligne.

En supposant un seul thread, cela signifie que le serveur doit synchroniser certaines données sur disque pour TOUS LES RANGES. Il doit attendre que les données atteignent un emplacement de stockage persistant (espérons que le ram sauvegardé par batterie dans votre contrôleur RAID). Cela est intrinsèquement plutôt lent et deviendra probablement le facteur limitant dans ces cas.

Je suppose bien sûr que vous utilisez un moteur transactionnel (généralement innodb) ET que vous n'avez pas modifié les paramètres pour réduire la durabilité.

Je suppose également que vous utilisez un seul thread pour faire ces insertions. L'utilisation de plusieurs threads complique quelque peu les choses car certaines versions de MySQL ont la validation de groupe de travail dans innodb. Cela signifie que plusieurs threads effectuant leurs propres validations peuvent partager une seule écriture dans le journal des transactions. .

En revanche, vous voulez VRAIMENT UTILISER des inserts à plusieurs rangées.

Il y a une limite pour laquelle cela devient contre-productif, mais dans la plupart des cas, il faut au moins 10 000 lignes. Donc, si vous les mettez en lot jusqu'à 1 000 lignes, vous êtes probablement en sécurité.

Si vous utilisez MyISAM, il y a une foule d'autres choses, mais je ne vous ennuierai pas avec celles-ci. Paix.

17
MarkR

Envoyez autant d'inserts sur le fil à la fois que possible. La vitesse d'insertion réelle devrait être la même, mais vous constaterez des gains de performances liés à la réduction des frais généraux du réseau.

8
RC.

En général, moins il y a d'appels dans la base de données, mieux c'est (plus rapide, plus efficace), essayez donc de coder les insertions de manière à minimiser les accès à la base de données. N'oubliez pas que, sauf si vous utilisez un pool de connexions, chaque accès à une base de données doit créer une connexion, exécuter le programme SQL, puis rompre la connexion. Un peu de frais généraux!

7
ennuikiller

Tu pourrais vouloir :

  • Vérifier que la validation automatique est désactivée
  • Connexion ouverte
  • Envoyer plusieurs lots d'insertions en une seule transaction (taille d'environ 4000-10000 lignes? Vous voyez)
  • Fermer la connexion

En fonction de la capacité de votre serveur à évoluer (c'est définitivement correct avec PostgreSQl, Oracle et MSSQL), procédez comme ci-dessus avec plusieurs threads et plusieurs connexions.

4
Antibarbie

En général, les insertions multiples seront plus lentes à cause de la surcharge de la connexion. Faire plusieurs insertions à la fois réduira le coût des frais généraux par insertion.

Selon la langue que vous utilisez, vous pouvez éventuellement créer un lot dans votre langage de programmation/script avant de passer à la base de données et d’ajouter chaque insertion au lot. Vous pourrez alors exécuter un gros lot en utilisant une opération de connexion. Voici un exemple en Java.

3
Chris Williams

MYSQL 5.5 Une instruction d'insertion SQL a pris environ 300 à ~ 450 ms. tandis que les statistiques ci-dessous concernent les déclarations d'insertion multiples en ligne.

(25492 row(s) affected)
Execution Time : 00:00:03:343
Transfer Time  : 00:00:00:000
Total Time     : 00:00:03:343

Je dirais que inline est la voie à suivre :)

3
A_01

Il est ridicule de voir comment Mysql et MariaDB sont optimisés en ce qui concerne les insertions. J'ai testé mysql 5.7 et mariadb 10.3, aucune différence réelle entre eux.

J'ai testé cela sur un serveur avec des disques NVME, 70 000 IOPS, un débit de seq de 1,1 Go/s et c'est possible en duplex intégral (lecture et écriture).
Le serveur est également un serveur hautes performances.
Lui a donné 20 Go de bélier.
La base de données est complètement vide.

La vitesse que je recevais était de 5000 insertions par seconde lorsque j'effectuais des insertions sur plusieurs lignes (essayé avec 1 Mo jusqu'à 10 Mo de morceaux de données)

Maintenant l'indice:
Si j'ajoute un autre thread et que je l'insère dans les tables SAME, j'ai soudainement 2x5000/s Un fil de plus et j'ai 15000 total/s

Tenez compte de ceci: lorsque ONE thread insère, cela signifie que vous pouvez écrire séquentiellement sur le disque (avec des exceptions pour les index). Lorsque vous utilisez des threads, vous réduisez réellement les performances possibles, car il doit maintenant effectuer beaucoup plus d’accès aléatoires. Mais la réalité montre que mysql est tellement mal optimisé que les threads aident beaucoup.

La performance réelle possible avec un tel serveur est probablement de plusieurs millions par seconde, le processeur est inactif, le disque est inactif.
La raison en est clairement que mariadb, tout comme mysql, a des délais internes.

1
John

les insertions multiples sont plus rapides mais cela a été fait. un autre problème est la désactivation des contraintes, les contrôles temprorary rendent les insertions beaucoup plus rapides. Peu importe que votre table l’ait ou non. Par exemple, testez la désactivation des clés étrangères et profitez de la vitesse:

SET FOREIGN_KEY_CHECKS=0;

bien sûr, vous devriez le réactiver après les insertions en:

SET FOREIGN_KEY_CHECKS=1;

c'est un moyen courant d'insérer d'énormes données. L'intégrité des données peut casser, vous devez donc vous en occuper avant de désactiver les vérifications de clé étrangère.

0
MSS