Cette question est republiée de Stack Overflow sur la base d'une suggestion dans les commentaires, excuses pour la duplication.
Des questions
Question 1: à mesure que la taille de la table de base de données augmente, comment puis-je régler MySQL pour augmenter la vitesse de l'appel LOAD DATA INFILE?
Question 2: utiliser un cluster d'ordinateurs pour charger différents fichiers csv, améliorer les performances ou les tuer? (c'est ma tâche de benchmarking pour demain en utilisant les données de chargement et les inserts en vrac)
Objectif
Nous essayons différentes combinaisons de détecteurs de caractéristiques et de paramètres de clustering pour la recherche d'images, nous devons donc être en mesure de créer de grandes bases de données en temps opportun.
Informations sur la machine
La machine a 256 gig de RAM et il y a 2 autres machines disponibles avec la même quantité de RAM s'il existe un moyen d'améliorer le temps de création en distribuant la base de données?
Schéma de table
le schéma de la table ressemble
+---------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+------------------+------+-----+---------+----------------+
| match_index | int(10) unsigned | NO | PRI | NULL | |
| cluster_index | int(10) unsigned | NO | PRI | NULL | |
| id | int(11) | NO | PRI | NULL | auto_increment |
| tfidf | float | NO | | 0 | |
+---------------+------------------+------+-----+---------+----------------+
créé avec
CREATE TABLE test
(
match_index INT UNSIGNED NOT NULL,
cluster_index INT UNSIGNED NOT NULL,
id INT NOT NULL AUTO_INCREMENT,
tfidf FLOAT NOT NULL DEFAULT 0,
UNIQUE KEY (id),
PRIMARY KEY(cluster_index,match_index,id)
)engine=innodb;
Analyse comparative jusqu'à présent
La première étape consistait à comparer les insertions en masse par rapport au chargement à partir d'un fichier binaire dans une table vide.
It took: 0:09:12.394571 to do 4,000 inserts with 5,000 rows per insert
It took: 0:03:11.368320 seconds to load 20,000,000 rows from a csv file
Compte tenu de la différence de performances, je suis allé avec le chargement des données à partir d'un fichier CSV binaire, j'ai d'abord chargé des fichiers binaires contenant 100K, 1M, 20M, 200M lignes en utilisant l'appel ci-dessous.
LOAD DATA INFILE '/mnt/tests/data.csv' INTO TABLE test;
J'ai tué le fichier binaire de 200 millions de lignes (~ 3 Go de fichier csv) après 2 heures.
J'ai donc exécuté un script pour créer la table, et insérer différents nombres de lignes à partir d'un fichier binaire puis déposer la table, voir le graphique ci-dessous.
Il a fallu environ 7 secondes pour insérer 1 million de lignes du fichier binaire. Ensuite, j'ai décidé de comparer l'insertion de 1 M de lignes à la fois pour voir s'il y aurait un goulot d'étranglement à une taille de base de données particulière. Une fois que la base de données a atteint environ 59 millions de lignes, le temps d'insertion moyen est tombé à environ 5 000 / seconde.
La définition de la clé globale key_buffer_size = 4294967296 a légèrement amélioré les vitesses d'insertion de petits fichiers binaires. Le graphique ci-dessous montre les vitesses pour différents nombres de lignes
Cependant, pour l'insertion de lignes 1M, cela n'a pas amélioré les performances.
lignes: 1 000 000 temps: 0: 04: 13,761428 insertions / s: 3 940
vs pour une base de données vide
lignes: 1 000 000 temps: 0: 00: 6,339295 insertions / sec: 315,492
Mise à jour
Faire les données de chargement en utilisant la séquence suivante vs simplement en utilisant la commande de chargement de données
SET autocommit=0;
SET foreign_key_checks=0;
SET unique_checks=0;
LOAD DATA INFILE '/mnt/imagesearch/tests/eggs.csv' INTO TABLE test_ClusterMatches;
SET foreign_key_checks=1;
SET unique_checks=1;
COMMIT;
Cela semble donc assez prometteur en termes de taille de base de données qui est générée, mais les autres paramètres ne semblent pas affecter les performances de l'appel de chargement des données.
J'ai ensuite essayé de charger plusieurs fichiers à partir de différentes machines, mais la commande de chargement des données infile verrouille la table, en raison de la grande taille des fichiers entraînant l'expiration des autres machines avec
ERROR 1205 (HY000) at line 1: Lock wait timeout exceeded; try restarting transaction
Augmenter le nombre de lignes dans un fichier binaire
rows: 10,000,000 seconds rows: 0:01:36.545094 inserts/sec: 103578.541236
rows: 20,000,000 seconds rows: 0:03:14.230782 inserts/sec: 102970.29026
rows: 30,000,000 seconds rows: 0:05:07.792266 inserts/sec: 97468.3359978
rows: 40,000,000 seconds rows: 0:06:53.465898 inserts/sec: 96743.1659866
rows: 50,000,000 seconds rows: 0:08:48.721011 inserts/sec: 94567.8324859
rows: 60,000,000 seconds rows: 0:10:32.888930 inserts/sec: 94803.3646283
Solution: précalculer l'ID en dehors de MySQL au lieu d'utiliser l'incrémentation automatique
Construire la table avec
CREATE TABLE test (
match_index INT UNSIGNED NOT NULL,
cluster_index INT UNSIGNED NOT NULL,
id INT NOT NULL ,
tfidf FLOAT NOT NULL DEFAULT 0,
PRIMARY KEY(cluster_index,match_index,id)
)engine=innodb;
avec le SQL
LOAD DATA INFILE '/mnt/tests/data.csv' INTO TABLE test FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';"
Obtenir le script pour précalculer les index semble avoir supprimé le hit de performance à mesure que la base de données grandit.
Mise à jour 2 - utilisation des tables de mémoire
Environ 3 fois plus rapide, sans tenir compte du coût de déplacement d'une table en mémoire vers une table sur disque.
rows: 0 seconds rows: 0:00:26.661321 inserts/sec: 375075.18851
rows: 10000000 time: 0:00:32.765095 inserts/sec: 305202.83857
rows: 20000000 time: 0:00:38.937946 inserts/sec: 256818.888187
rows: 30000000 time: 0:00:35.170084 inserts/sec: 284332.559456
rows: 40000000 time: 0:00:33.371274 inserts/sec: 299658.922222
rows: 50000000 time: 0:00:39.396904 inserts/sec: 253827.051994
rows: 60000000 time: 0:00:37.719409 inserts/sec: 265115.500617
rows: 70000000 time: 0:00:32.993904 inserts/sec: 303086.291334
rows: 80000000 time: 0:00:33.818471 inserts/sec: 295696.396209
rows: 90000000 time: 0:00:33.534934 inserts/sec: 298196.501594
en chargeant les données dans une table basée sur la mémoire, puis en les copiant sur une table basée sur le disque en morceaux, le temps système était de 10 min 59,71 s pour copier 107 356 741 lignes avec la requête
insert into test Select * from test2;
ce qui fait environ 15 minutes pour charger 100 millions de lignes, ce qui équivaut approximativement à l'insérer directement dans une table sur disque.
id
devrait être plus rapide. (Bien que je pense que vous ne cherchez pas cela)Réponses:
Bonne question - bien expliquée.
Vous avez déjà un paramètre élevé (ish) pour le tampon de clé - mais est-ce suffisant? Je suppose que c'est une installation 64 bits (sinon la première chose que vous devez faire est de mettre à niveau) et ne fonctionne pas sur MSNT. Jetez un œil à la sortie de mysqltuner.pl après avoir exécuté quelques tests.
Afin d'utiliser le cache au mieux, vous pouvez trouver des avantages à regrouper / pré-trier les données d'entrée (les versions les plus récentes de la commande 'sort' ont beaucoup de fonctionnalités pour trier de grands ensembles de données). De plus, si vous générez des numéros d'identification en dehors de MySQL, cela peut être plus efficace.
En supposant (encore une fois) que vous voulez que le jeu de sortie se comporte comme une seule table, les seuls avantages que vous obtiendrez seront de distribuer le travail de tri et de génération d'ID - pour lesquels vous n'avez pas besoin de plus de bases de données. OTOH en utilisant un cluster de base de données, vous aurez des problèmes de contention (que vous ne devriez pas voir autrement que comme des problèmes de performances).
Si vous pouvez scinder les données et gérer les ensembles de données résultants de manière indépendante, alors oui, vous obtiendrez des avantages en termes de performances, mais cela n'élimine pas la nécessité de régler chaque nœud.
Vérifiez que vous disposez d'au moins 4 Go pour le sort_buffer_size.
Au-delà de cela, le facteur limitant des performances concerne les E / S disque. Il existe de nombreuses façons de résoudre ce problème, mais vous devriez probablement envisager un ensemble en miroir d'ensembles de données agrégées par bandes sur les disques SSD pour des performances optimales.
la source
load data...
c'est plus rapide que l'insertion, alors utilisez-le.Si vous voulez être vraiment flippant, vous pouvez créer un programme multi-thread pour alimenter un seul fichier vers une collection de canaux nommés et gérer les instances d'insertion.
En résumé, vous ne réglez pas MySQL pour autant que vous réglez votre charge de travail sur MySQL.
la source
Je ne me souviens pas exactement de la syntaxe, mais si ce n'est pas le cas, vous pouvez désactiver la vérification des clés étrangères.
Vous pouvez également créer l'index après l'importation, cela peut être vraiment un gain de performances.
la source