Psycopg2, Postgresql, Python: le moyen le plus rapide de bulk-insert

je cherche le moyen le plus efficace d'insérer des millions de tuples dans une base de données. J'utilise Python, PostgreSQL et psycopg2 .

j'ai créé une longue liste de tulpesqui doivent être insérés dans la base de données, parfois avec des modificateurs comme géométrique Simplify .

la façon naïve de le faire serait de formater une liste de déclarations INSERT , mais il y a trois autres méthodes que j'ai lu à propos de:

  1. à l'Aide pyformat liaison style pour paramétrique d'insertion
  2. utilisant executemany sur la liste des tuples, et
  3. en utilisant l'écriture des résultats dans un fichier et en utilisant COPY .

il semble que la première voie soit la plus efficace, mais j'apprécierais vos intuitions et vos bribes de code me disant comment le faire correctement.

36
demandé sur Community 2010-02-16 12:36:50

8 réponses

Oui, je voterais pour la copie, à condition que vous puissiez écrire un fichier sur le disque dur du serveur (pas le disque sur lequel l'application est lancée) car la copie ne lira que sur le serveur.

13
répondu Andy Shellam 2010-02-16 09:41:02

il y a un nouveau manuel psycopg2 contenant des exemples pour toutes les options.

l'option copie est la plus efficace. Puis le executemany. Puis l'exécution avec pyformat.

10
répondu piro 2010-02-16 10:18:57

d'après mon expérience, executemany n'est pas plus rapide que de lancer plusieurs inserts vous-même., la manière la plus rapide est de formater un seul INSERT avec beaucoup de valeurs vous-même, peut-être à l'avenir executemany s'améliorera, mais pour l'instant il est assez lent

i sous-classe A list et surcharger la méthode d'ajout, de sorte que lorsque a la liste atteint une certaine taille, je formate L'insertion pour l'exécuter

7
répondu FlashDD 2014-02-02 12:14:24

, Vous pourriez utiliser une nouvelle upsert bibliothèque :

$ pip install upsert

(vous pourriez avoir à pip install decorator d'abord)

conn = psycopg2.connect('dbname=mydatabase')
cur = conn.cursor()
upsert = Upsert(cur, 'mytable')
for (selector, setter) in myrecords:
    upsert.row(selector, setter)

selector est un dict objet comme {'name': 'Chris Smith'} et setter est un dict comme { 'age': 28, 'state': 'WI' }

c'est presque aussi vite que d'écrire le code personnalisé[/UPDATE] et de l'exécuter directement avec psycopg2 ... et il ça n'explosera pas si la rangée existe déjà.

6
répondu Seamus Abshere 2012-09-27 01:27:02

le premier et le second seraient utilisés ensemble, et non séparément. Le troisième serait le plus efficace du point de vue du serveur, puisque le serveur ferait tout le travail dur.

1
répondu Ignacio Vazquez-Abrams 2010-02-16 09:39:15

après quelques tests, unnest semble souvent être une option extrêmement rapide, comme j'ai appris de @Clodoaldo Neto 's réponse à une question similaire.

data = [(1, 100), (2, 200), ...]  # list of tuples

cur.execute("""CREATE TABLE table1 AS
               SELECT u.id, u.var1
               FROM unnest(%s) u(id INT, var1 INT)""", (data,))

cependant, il peut être délicat avec des données extrêmement grandes .

1
répondu n1000 2017-05-23 12:18:09

N'importe qui utilisant SQLalchemy pourrait essayer la version 1.2 qui a ajouté le soutien de insertion en vrac pour utiliser psycopg2.supplémentaire.execute_batch () au lieu de executemany lorsque vous initialisez votre moteur avec use_batch_mode=True like:

engine = create_engine(
    "postgresql+psycopg2://scott:tiger@host/dbname",
    use_batch_mode=True)

http://docs.sqlalchemy.org/en/latest/changelog/migration_12.html#change-4109

alors quelqu'un devrait utiliser SQLalchmey ne sera pas la peine d'essayer différentes combinaisons de sqla et psycopg2 et direct SQL together.

1
répondu user2189731 2018-01-02 02:06:31

une question très liée: vrac insert avec SQLAlchemy ORM


toutes les routes mènent à Rome , mais certains d'entre eux traverse les montagnes, nécessite des ferries, mais si vous voulez y arriver rapidement, il suffit de prendre l'autoroute.


dans ce cas, l'autoroute doit utiliser le execute_batch () caractéristique de psycopg2 . La documentation le dit le mieux:

la mise en œuvre actuelle de executemany() n'est pas particulièrement performante (selon un euphémisme extrêmement charitable). Ces fonctions peuvent être utilisées pour accélérer l'exécution répétée d'un énoncé par rapport à un ensemble de paramètres. En réduisant le nombre de roundtrips de serveur la performance peut être des ordres de grandeur mieux que d'utiliser executemany() .

dans mon propre test execute_batch() est environ deux fois plus rapide que executemany() , et donne l'option de configurer le page_size pour de nouveaux réglages (si vous voulez presser les derniers 2-3% de performance du pilote).

la même fonctionnalité peut facilement être activée si vous utilisez SQLAlchemy en paramétrant use_batch_mode=True comme paramètre lorsque vous instanciez le moteur avec create_engine()

0
répondu chjortlund 2018-06-13 14:27:06