2011-06-26 4 views
19

Je travaille avec le module MySQLdb en Python pour interagir avec une base de données. J'ai une situation où il y a une très grande liste (des dizaines de milliers d'éléments) que j'ai besoin d'insérer en tant que lignes dans une table.Python + MySQL - Bulk Insert

Ma solution est maintenant de générer une grande déclaration INSERT comme une chaîne et l'exécuter.

Y a-t-il un moyen plus intelligent?

Répondre

16

Il existe un moyen plus intelligent. Le problème avec les insertions en masse est que, par défaut, autocommit is enabled entraîne l'enregistrement de chaque instruction insert dans la mémoire stable avant que l'insertion suivante puisse être lancée.

Comme les notes de page de manuel:

Par défaut, MySQL fonctionne avec le mode autocommit activé. Cela signifie que dès que vous exécutez une instruction que met à jour (modifie) une table, MySQL stocke la mise à jour sur le disque pour la rendre permanente. Pour désactiver le mode de validation automatique, utilisez l'instruction suivante:

SET autocommit=0; 

Après avoir désactivé mode de validation automatique en réglant la variable autocommit à zéro, les modifications aux tables de transaction de sécurité (tels que ceux pour InnoDB , BDB ou NDBCLUSTER) ne sont pas rendus permanents immédiatement. Vous devez utiliser COMMIT pour stocker vos modifications sur le disque ou ROLLBACK pour ignorer les modifications.

Ceci est une caractéristique assez commune des systèmes RDBM qui supposent que l'intégrité de la base de données est primordiale. Cela permet de prendre des insertions en gros de l'ordre de 1s par insert au lieu de 1ms. L'alternative de créer une instruction d'insertion overgrand tente d'obtenir ce commit unique au risque de surcharger l'analyseur SQL.

+4

Depuis la version 1.2.0, MySQLdb désactive la validation automatique par défaut, comme requis par la norme DB-API (PEP-249). source: http://mysql-python.sourceforge.net/FAQ.html – mikewaters

+0

Si vous pensez que vos inserts sont encore plus lents qu'ils ne devraient l'être, assurez-vous également de modifier les paramètres de votre serveur mysql. Dans mon cas, mon innodb_buffer_pool_size était trop petit pour mes transactions et en l'augmentant, j'ai atteint une accélération de + 40% pour les encarts en vrac. Voir: https://dev.mysql.com/doc/refman/5.7/en/innodb-buffer-pool.html – jlh

1

Tant que vous le faites en un seul INSERT et non pas des milliers de ceux individuels, alors oui c'est la meilleure façon de le faire. Attention à ne pas dépasser la taille maximale des paquets de mysqls, et ajustez-la si nécessaire. Par exemple, cela définit le paquet de serveur max à 32Mb. Vous devez faire la même chose sur le client aussi.

mysqld --max_allowed_packet=32M 
+0

c'est un hack autour du mécanisme de transaction qui adresse le symptôme et non la cause – msw

+0

Pourriez-vous développer un peu sur cela? Ou dites ce que vous feriez à la place? – justinhj

+0

@msw Il est toujours plus rapide d'émettre des INSERT * par lots avec des transactions ... – Will

11

Si vous devez insérer une très grande quantité de données, pourquoi essayez-vous de les insérer tous en un seul insert? (Cela mettra forcément en charge votre mémoire en faisant cette grande chaîne insert et aussi en l'exécutant.En outre, ce n'est pas une très bonne solution si vos données à insérer est très très grande.)

Pourquoi ne pas vous mettez une ligne par commande insert dans la base de données et mettez toutes les lignes en utilisant un for...loop et validez tous les changements à la fin?

con = mysqldb.connect(
         host="localhost", 
         user="user", 
         passwd="**", 
         db="db name" 
        ) 
cur = con.cursor() 

for data in your_data_list: 
    cur.execute("data you want to insert: %s" %data) 

con.commit() 
con.close() 

(Croyez-moi, ce qui est vraiment rapide mais si vous obtenez des résultats plus lents alors cela signifie que votre autocommit doit être True.Réglez-le à False comme msw dit.)

+0

S'il y a une déclaration pour chaque insertion, cela ne serait-il pas très lent? Cela ne me dérange pas d'utiliser la mémoire. Ce ne sera que des mégaoctets, donc je ne suis pas concerné. – Mike

+0

non ça ne sera pas lent c'est ce que je veux dire ... tant que vous ne vous engagez pas entre la boucle. Essayez les deux et voyez si vous ne me croyez pas ... –

+0

Dans MyISAM (le moteur que j'utilise) ne commet pas implicitement après les exécutions? – Mike