J'ai du mal à importer en masse une assez grande table InnoDB composée d'environ 10 millions de lignes (ou 7 Go) (qui est pour moi la plus grande table avec laquelle j'ai travaillé jusqu'à présent).
J'ai fait des recherches pour améliorer la vitesse d'importation d'Inno et pour l'instant ma configuration ressemble à ceci:
/etc/mysql/my.cnf/
[...]
innodb_buffer_pool_size = 7446915072 # ~90% of memory
innodb_read_io_threads = 64
innodb_write_io_threads = 64
innodb_io_capacity = 5000
innodb_thread_concurrency=0
innodb_doublewrite = 0
innodb_log_file_size = 1G
log-bin = ""
innodb_autoinc_lock_mode = 2
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit=2
innodb_buffer_pool_instances=8
import is done via bash script, here is the mysql code:
SET GLOBAL sync_binlog = 1;
SET sql_log_bin = 0;
SET FOREIGN_KEY_CHECKS = 0;
SET UNIQUE_CHECKS = 0;
SET AUTOCOMMIT = 0;
SET SESSION tx_isolation='READ-UNCOMMITTED';
LOAD DATA LOCAL INFILE '$filepath' INTO TABLE monster
COMMIT;
Les données sont fournies dans un CSV
fichier.
Actuellement, je teste mes paramètres avec de plus petits «vidages de test» avec 2 millions, 3 millions,… lignes chacun et j'utilise time import_script.sh
pour comparer les performances.
L'inconvénient est que je ne dispose que d'un temps de fonctionnement global, je dois donc attendre la fin de l'importation complète pour obtenir un résultat.
Mes résultats jusqu'à présent:
- 10000 lignes: <1 seconde
- 100 000 lignes: 10 secondes
- 300 000 lignes: 40 secondes
- 2 millions de lignes: 18 minutes
- 3 millions de lignes: 26 minutes
- 4 millions de lignes: (annulé après 2 heures)
Il semble qu'il n'y ait pas de solution de «livre de cuisine» et il faut trouver par lui-même la combinaison optimale de paramètres.
Outre des suggestions sur ce qu'il faut changer dans ma configuration, j'apprécierais également plus d'informations sur la manière de mieux évaluer le processus d'importation / d'avoir plus d'informations sur ce qui se passe et où se trouve le goulot d'étranglement.
J'ai essayé de lire la documentation des paramètres que je modifie, mais là encore je ne suis pas au courant d'effets secondaires et si je pouvais même diminuer les performances avec une valeur mal choisie.
Pour le moment, je voudrais essayer une suggestion de chat à utiliser MyISAM
lors de l'importation et changer le moteur de table par la suite.
J'aimerais essayer ceci mais pour le moment ma DROP TABLE
requête prend également des heures pour se terminer. (Ce qui semble être un autre indicateur que mon réglage est moins qu'optimal).
Informations supplémentaires:
La machine que j'utilise actuellement dispose de 8 Go de RAM et d'un disque dur hybride à semi-conducteurs avec 5400 tr / min.
Bien que nous cherchions également à supprimer les données obsolètes du tableau en question, j'ai encore besoin d'une importation assez rapide vers
a) le test automatic data cleanup feature
pendant le développement et
b) au cas où notre serveur se bloquerait, nous aimerions utiliser notre 2ème serveur comme remplacement (qui a besoin de plus de données à jour, la dernière importation a pris plus de 24 heures)
mysql> SHOW CREATE TABLE monster\G
*************************** 1. row ***************************
Table: monster
Create Table: CREATE TABLE `monster` (
`monster_id` int(11) NOT NULL AUTO_INCREMENT,
`ext_monster_id` int(11) NOT NULL DEFAULT '0',
`some_id` int(11) NOT NULL DEFAULT '0',
`email` varchar(250) NOT NULL,
`name` varchar(100) NOT NULL,
`address` varchar(100) NOT NULL,
`postcode` varchar(20) NOT NULL,
`city` varchar(100) NOT NULL,
`country` int(11) NOT NULL DEFAULT '0',
`address_hash` varchar(250) NOT NULL,
`lon` float(10,6) NOT NULL,
`lat` float(10,6) NOT NULL,
`ip_address` varchar(40) NOT NULL,
`cookie` int(11) NOT NULL DEFAULT '0',
`party_id` int(11) NOT NULL,
`status` int(11) NOT NULL DEFAULT '2',
`creation_date` datetime NOT NULL,
`someflag` tinyint(1) NOT NULL DEFAULT '0',
`someflag2` tinyint(4) NOT NULL,
`upload_id` int(11) NOT NULL DEFAULT '0',
`news1` tinyint(4) NOT NULL DEFAULT '0',
`news2` tinyint(4) NOT NULL,
`someother_id` int(11) NOT NULL DEFAULT '0',
`note` varchar(2500) NOT NULL,
`referer` text NOT NULL,
`subscription` int(11) DEFAULT '0',
`hash` varchar(32) DEFAULT NULL,
`thumbs1` int(11) NOT NULL DEFAULT '0',
`thumbs2` int(11) NOT NULL DEFAULT '0',
`thumbs3` int(11) NOT NULL DEFAULT '0',
`neighbours` tinyint(4) NOT NULL DEFAULT '0',
`relevance` int(11) NOT NULL,
PRIMARY KEY (`monster_id`),
KEY `party_id` (`party_id`),
KEY `creation_date` (`creation_date`),
KEY `email` (`email`(4)),
KEY `hash` (`hash`(8)),
KEY `address_hash` (`address_hash`(8)),
KEY `thumbs3` (`thumbs3`),
KEY `ext_monster_id` (`ext_monster_id`),
KEY `status` (`status`),
KEY `note` (`note`(4)),
KEY `postcode` (`postcode`),
KEY `some_id` (`some_id`),
KEY `cookie` (`cookie`),
KEY `party_id_2` (`party_id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=13763891 DEFAULT CHARSET=utf8
la source
SHOW CREATE TABLE yourtable\G
pour nous montrer la structure du tableau de ce tableau de 10 millions de lignes.innodb_doublewrite = 0
), votre installation MySQL n'est pas protégée contre les pannes: si vous avez une panne de courant (pas une panne MySQL), vos données pourraient être silencieusement corrompues.Réponses:
Tout d'abord, vous devez savoir ce que vous faites à InnoDB lorsque vous labourez des millions de lignes dans une table InnoDB. Jetons un coup d'œil à l'architecture InnoDB.
Dans le coin supérieur gauche, il y a une illustration du pool de tampons InnoDB. Notez qu'il y a une section dédiée au tampon d'insertion. Qu'est-ce que ça fait? Il est destiné à migrer les modifications apportées aux index secondaires du pool de tampons vers le tampon d'insertion à l'intérieur de l'espace disque logique du système (alias ibdata1). Par défaut, innodb_change_buffer_max_size est défini sur 25. Cela signifie que jusqu'à 25% du pool de tampons peuvent être utilisés pour traiter les index secondaires.
Dans votre cas, vous disposez de 6,935 Go pour le pool de mémoire tampon InnoDB. Un maximum de 1,734 Go sera utilisé pour traiter vos index secondaires.
Maintenant, regardez votre table. Vous disposez de 13 index secondaires. Chaque ligne que vous traitez doit générer une entrée d'index secondaire, la coupler avec la clé primaire de la ligne et les envoyer en tant que paire à partir du tampon d'insertion dans le pool de tampons dans le tampon d'insertion dans ibdata1. Cela se produit 13 fois avec chaque ligne. Multipliez cela par 10 millions et vous pouvez presque sentir un goulot d'étranglement venir.
N'oubliez pas que l'importation de 10 millions de lignes en une seule transaction empilera tout dans un segment d'annulation et remplira l'espace UNDO dans ibdata1.
SUGGESTIONS
SUGGESTION # 1
Ma première suggestion pour importer ce tableau assez grand serait
SUGGESTION # 2
Débarrassez-vous des index en double. Dans votre cas, vous avez
Les deux index commencent par
party_id
, vous pouvez augmenter le traitement des index secondaires d'au moins 7,6% en supprimant un index sur 13. Vous devez éventuellement exécuterSUGGESTION # 3
Débarrassez-vous des index que vous n'utilisez pas. Examinez le code de votre application et voyez si vos requêtes utilisent tous les index. Vous voudrez peut-être examiner l'utilisation de pt-index pour lui permettre de suggérer quels index ne sont pas utilisés.
SUGGESTION # 4
Vous devez augmenter la taille innodb_log_buffer_size à 64 Mo, car la valeur par défaut est 8 Mo. Un tampon de journal plus important peut augmenter les performances d'E / S d'écriture d'InnoDB.
ÉPILOGUE
Pour mettre en place les deux premières suggestions, procédez comme suit:
party_id
indexPeut-être que ce qui suit peut aider
Importez les données dans
monster
. Ensuite, exécutez cetteESSAIE !!!
ALTERNATIVE
Vous pouvez créer une table appelée en
monster_csv
tant que table MyISAM sans index et procédez comme suit:Importez vos données dans
monster_csv
. Ensuite, utilisez mysqldump pour créer une autre importationLe fichier mysqldump
data.sql
étendra les commandes INSERT en important 10 000 à 20 000 lignes à la fois.Maintenant, chargez juste le mysqldump
Enfin, débarrassez-vous de la table MyISAM
la source
monster
table) en moins de 20 minutes lorsque je n'avais pas de clés sur les tables InnoDB. L'ajout de clés a pris environ. encore 20 min. Je dirais que cela résout à peu près mon problème dans ce cas. Merci beaucoup!Je voulais écrire un commentaire (car ce n'est pas une réponse définitive), mais c'est devenu trop long:
Je vais vous donner plusieurs conseils généraux, et nous pouvons entrer dans les détails de chacun, si vous le souhaitez:
N'oubliez pas que certains d'entre eux ne sont pas sécurisés ou recommandés pour les non-importations (fonctionnement normal).
la source
SET SESSION tx_isolation='READ-UNCOMMITTED';
(uniquement utile si vous importez avec plusieurs threads en parallèle) et le commentaire @ypercube sur l'insertion dans des lots. Vous avez un exemple complet ici: mysqlperformanceblog.com/2008/07/03/… Assurez-vous que vous bénéficiez de toutes les fonctionnalités des dernières versions d'InnoDB: mysqlperformanceblog.com/2011/01/07/…La plupart des bons conseils ont été donnés jusqu'à présent, mais sans beaucoup d'explications pour les meilleurs. Je donnerai plus de détails.
Tout d'abord, retarder la création d'un index est une bonne chose, avec suffisamment de détails dans d'autres réponses. Je n'y reviendrai pas.
Un fichier journal InnoDB plus grand vous aidera beaucoup (si vous utilisez MySQL 5.6 car il n'est pas possible de l'augmenter dans MySQL 5.5). Vous insérez 7 Go de données, je recommanderais une taille totale de journal d'au moins 8 Go (conservez
innodb_log_files_in_group
sa valeur par défaut (2) et augmentezinnodb_log_file_size
à 4 Go). Ce 8 Go n'est pas exact: il devrait être au moins de la taille d'importation dans le journal REDO et probablement doubler ou quadrupler cette taille. Le raisonnement derrière la taille du journal InnoDB augmente le fait que lorsque le journal deviendra presque plein, InnoDB commencera à vider agressivement son pool de tampons sur le disque pour éviter que le journal ne se remplisse (lorsque le journal est plein, InnoDB ne peut faire aucune écriture de base de données jusqu'à ce que certains pages du pool de mémoire tampon sont écrites sur le disque).Un fichier journal InnoDB plus grand vous sera utile, mais vous devez également insérer dans l'ordre des clés primaires (trier votre fichier avant l'insertion). Si vous insérez dans l'ordre des clés primaires, InnoDB remplira une page, puis une autre, et ainsi de suite. Si vous n'insérez pas dans l'ordre des clés primaires, votre prochaine insertion peut se retrouver dans une page pleine et entraîner un "fractionnement de page". Ce fractionnement de page coûtera cher à InnoDB et ralentira votre importation.
Vous avez déjà un pool de tampons aussi grand que votre RAM vous le permet et si votre table ne s'y adapte pas, vous ne pouvez pas faire grand-chose à part acheter plus de RAM. Mais si votre table tient dans le pool de tampons mais est supérieure à 75% de votre pool de tampons, vous pouvez essayer d'augmenter
innodb_max_dirty_pages_pct
à 85 ou 95 lors de l'importation (la valeur par défaut est 75). Ce paramètre de configuration indique à InnoDB de commencer à vider agressivement le pool de tampons lorsque le pourcentage de pages sales atteint cette limite. En augmentant ce paramètre (et si vous avez de la chance sur la taille des données), vous pouvez éviter les E / S agressives lors de votre importation et retarder ces E / S plus tard.Peut-être (et c'est une supposition) que l'importation de vos données dans de nombreuses petites transactions vous aidera. Je ne sais pas exactement comment le journal REDO est construit, mais s'il est mis en mémoire tampon dans la RAM (et sur le disque quand trop de RAM serait nécessaire) pendant que la transaction progresse, vous pourriez vous retrouver avec des E / S inutiles. Vous pouvez essayer ceci: une fois votre fichier trié, divisez-le en plusieurs morceaux (essayez avec 16 Mo et d'autres tailles) et importez-les un par un. Cela vous permettrait également de contrôler la progression de votre importation. Si vous ne souhaitez pas que vos données soient partiellement visibles par un autre lecteur pendant que vous effectuez votre importation, vous pouvez importer en utilisant un nom de table différent, créer les index ultérieurement, puis renommer la table.
À propos de votre disque SSD hybride / 5400 tr / min, je ne sais pas ceux-ci et comment l'optimiser. 5400 tr / min semble lent pour une base de données, mais peut-être que le SSD évite cela. Vous remplissez peut-être la partie SSD de votre disque avec des écritures séquentielles dans le journal REDO et le SSD nuit aux performances. Je ne sais pas.
Un mauvais conseil que vous ne devriez pas essayer (ou faire attention) est le suivant: n'utilisez pas de multi-thread: il sera très difficile d'optimiser pour éviter les sauts de page dans InnoDB. Si vous souhaitez utiliser plusieurs threads, insérez-les dans différentes tables (ou dans différentes partitions de la même table).
Si vous envisagez le multi-thread, vous avez peut-être un ordinateur multi-socket (NUMA). Dans ce cas, assurez-vous d'éviter le problème de folie du swap MySQL .
Si vous utilisez MySQL 5.5, passez à MySQL 5.6: il a la possibilité d'augmenter la taille du journal REDO et a de meilleurs algorithmes de vidage de pool de mémoire tampon.
Bonne chance avec votre importation.
la source