Comment puis-je améliorer mon instruction INSERT de performance?
Tandis que Josh réponse ici m'a donné une bonne longueur d'avance sur la façon d'insérer une 256x64x250 tableau de valeur dans une base de données MySQL. Quand j'ai essayé de son instruction INSERT sur mes données, il s'est avéré horriblement lent (comme dans 6 minutes pour un 16 mo de fichier).
ny, nx, nz = np.shape(data)
query = """INSERT INTO `data` (frame, sensor_row, sensor_col, value) VALUES (%s, %s, %s, %s)"""
for frames in range(nz):
for rows in range(ny):
for cols in range(nx):
cursor.execute(query, (frames, rows, cols, data[rows,cols,frames]))
Je lisais MySQL pour Python, qui a expliqué que ce n'était pas la bonne approche, car l'exécution de 4 millions de séparer les plaquettes est très inefficace.
Maintenant mes données se composent de beaucoup de zéros (plus de 90% en fait), donc je l'ai jeté dans une instruction if donc, je n'insérez des valeurs supérieures à zéro et j'ai utilisé executemany() à la place:
query = """INSERT INTO `data` (frame, sensor_row, sensor_col, value) VALUES (%s, %s, %s, %s ) """
values = []
for frames in range(nz):
for rows in range(ny):
for cols in range(nx):
if data[rows,cols,frames] > 0.0:
values.append((frames, rows, cols, data[rows,cols,frames]))
cur.executemany(query, values)
Ce miraculeusement apporté mon temps de traitement jusqu'à environ 20 secondes, dont 14 secondes passent sur la création de la liste de valeurs (37k lignes) et 4 secondes sur la réelle insertion dans la base de données.
Alors maintenant, je me demandais, comment puis-je accélérer ce processus davantage? Parce que j'ai le sentiment que ma boucle est horriblement inefficace et il y a une meilleure façon. Si j'ai besoin d'insérer des 30 mesures par chien, ce serait encore prendre 10 minutes, ce qui semble beaucoup trop long pour cette quantité de données.
Voici deux versions de mes fichiers raw: avec les en-têtes ou sans les en-têtes. J'aimerais essayer de le LOAD DATA INFILE, mais je ne peux pas comprendre comment analyser correctement les données.
- D'où viennent les données?
- C'est un gros fichier texte avec des en-têtes au-dessus de chaque cadre, qu'est-ce précisément que vous voulez savoir?
- Je suis juste curieux de savoir si le long processus de la prise de est de la lecture du fichier ou les boucles for imbriquées pour créer la requête.
- Qu'est-ce que
data
? Peut-être que vous pouvez obtenirvalues
par l'application d'une fonction surdata
? Cela pourrait vous donner des principaux speedup. - de données est un tableau numpy, qui est chargé dans la mémoire, donc je suppose qu'il s'agit de boucles imbriquées. Si il y a un autre moyen de récupérer toutes les non-valeurs zéro avec leurs index, alors je suis tout ouïe
- Gardez à l'esprit que le plus de [INSÉRER] les opérations sont effectuées, la probabilité de performances seront un sujet de préoccupation. Les bases de données d'écriture sur le disque - alors qu'il y a un tampon, il peut être épuisé. Aussi, vous voudrez peut-être regarder dans le MySQL LOAD DATA INFILE pour le chargement de fichiers, mais je ne pense pas qu'il prend en charge la logique de décision que vous utilisez pour lancer des données (pourquoi est-il connecté en premier lieu?).
- les données sont exportées à partir d'un autre logiciel, donc je n'ai pas d'influence sur le format. Je peux imaginer que le chargement d'un fichier directement dans la base de données serait encore plus vite, mais je ne suis pas sûr de savoir comment assurez-vous qu'il a le bon format.
- J'ai ajouté une modification à ma réponse originale à cette question qui pourrait être d'intérêt.
numpy.nonzero()
Vous devez vous connecter pour publier un commentaire.
Si les données est un tableau numpy, vous pouvez essayer ceci:
ou
Espère que cela aide
le moyen le plus rapide pour insérer 4 millions de lignes (16 mo de données) serait d'utiliser load data infile - http://dev.mysql.com/doc/refman/5.0/en/load-data.html
donc si possible de générer un fichier csv, puis utilisez les commandes load data infile..
espère que cela aide 🙂
MODIFIER
J'ai donc pris un de vos fichiers de données d'origine pente.dat et a écrit un rapide et sale programme pour convertir les suivantes format csv.
Télécharger les images.dat à partir d'ici: http://rapidshare.com/files/454896698/frames.dat
Images.dat
Le fichier contient des données uniquement pour les images qui ont des valeurs de chaque ligne et le col - de sorte que les zéros sont exclus. 24799 lignes de données ont été générées à partir de votre fichier d'origine.
Ensuite, j'ai créé un temporaire de chargement (mise en scène) de la table dans laquelle les cadres.dat fichier est chargé. C'est une table temporaire qui vous permettra de manipuler/transformer les données avant de les charger dans la bonne production/tableaux de présentation.
Tout ce qui reste est pour charger les données (remarque: je suis à l'aide de windows de sorte que vous aurez à modifier ce script pour le rendre linux compatible - vérifier les chemins d'accès et le changement "\r\n' à '\n')
24K lignes ont été chargés de 1,87 secondes.
Espère que cela aide 🙂
Je n'utilise pas de Python ou de mySQL, mais de lot de la performance de l'insert peut souvent être accéléré avec des de transactions.
Si je comprends bien, executemany() exécute une requête INSERT INTO pour chaque ligne que vous souhaitez insérer. Cela peut être amélioré par la création d'une seule requête d'INSERTION avec toutes les valeurs, qui devrait ressembler à ceci:
Votre code python devrait générer les valeurs de ligne de crochets et de créer une chaîne de requête pour enfin exécuter la requête une fois.
values.append((frames, rows, cols, data[rows,cols,frames]))
Le problème est, il est horriblement lent pour composer une liste de ce type et je n'ai aucune idée de comment l'optimiser('INSERT INTO
de données` (cadre, sensor_row, sensor_col, valeur) VALUES (%s, %s, %s, %s ) ', [(0, 31, 45, 0.40000001), (0, 31, 46, 0.40000001), (0, 32, 45, 0.40000001),`Insertion de plusieurs lignes sur chaque instruction est un moyen d'optimiser. Cependant, pourquoi la nécessité pour les 3 boucles? Peut-être une sorte de transformation de données pourrait être utile à la place.
Une autre option est de désactiver des index lors de l'insertion, si vous êtes certain que vous n'aurez pas de dupliquer les données (en supposant que vous avez réellement index sur la table). Les index doivent être mis à jour pour chaque instruction, et également vérifié pour éviter les doublons.
Invoquer
ALTER TABLE tablename DISABLE KEYS
avant de commencer vos inserts, et quand c'est fini invoquerALTER TABLE tablename ENABLE KEYS
et voir si ça aideÀ partir de la notice:
ALTER TABLE ... DISABLE KEYS dit à MySQL pour arrêter la mise à jour des index non unique. ALTER TABLE ... PERMETTRE à TOUCHES, puis devrait être utilisé pour re-créer des index manquants. MySQL fait avec un algorithme spécial qui est beaucoup plus rapide que l'insertion de clés une par une, si la désactivation des touches avant d'effectuer les opérations bulk insert doit donner une considérable accélération. À l'aide de l'instruction ALTER TABLE ... DÉSACTIVER les TOUCHES nécessite l'INDICE de privilège en plus des avantages mentionnés précédemment.
cur.execute("""ALTER TABLE data ENABLE KEYS""")
et a obtenu Table de moteur de stockage pour les "données" ne dispose pas de cette option Donc je suppose que je suis en train de faire quelque chose de mal.Vous pourriez employer liste comprehenshions au lieu de
for
boucles:Je l'estiment que cela pourrait vous donner une légère vitesse, telles que 10 à 20%.