Psycopg2, Postgresql, Python: самый быстрый способ массовой вставки

Я ищу наиболее эффективный способ массовой вставки нескольких миллионов кортежей в базу данных. Я использую Python, PostgreSQL и psycopg2.

Я создал длинный список tulpes, которые должны быть вставлены в базу данных, иногда с модификаторами, такими как geometric Simplify.

наивный способ сделать это будет строковое форматирование списка INSERT операторы, но есть три других метода, о которых я читал:

  1. используя pyformat привязка стиле для параметрического вставки
  2. используя executemany в списке кортежей и
  3. использование записи результатов в файл и использование COPY.

кажется, что первый способ наиболее эффективен, но я был бы признателен за ваши идеи и фрагменты кода, рассказывающие мне, как это сделать правильно.

8 ответов


Да, я бы проголосовал за копию, при условии, что вы можете написать файл в сервержесткий диск (не диск, на котором работает приложение), поскольку копия будет считываться только с сервера.


есть новый руководство psycopg2 содержит примеры для всех опций.

на скопировать вариант самый эффективный. Тогда executemany. Затем выполнить с pyformat.


по моему опыту executemany не быстрее, чем запуск многих вставок самостоятельно, самый быстрый способ-отформатировать один INSERT многие ценности, может в будущем executemany улучшится, но пока это довольно медленно

Я подкласс a list и перегрузить метод добавления, поэтому, когда список достигает определенного размера, я форматирую вставку, чтобы запустить ее


можно использовать новая библиотека upsert:

$ pip install upsert

(можно pip install decorator первая)

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

здесь selector - это dict объект {'name': 'Chris Smith'} и setter это dict как { 'age': 28, 'state': 'WI' }

это почти так же быстро, как писать пользовательский код вставки [/UPDATE] и запускать его напрямую с помощью psycopg2... и он не взорвется, если строка уже существует.


первый и второй будут использоваться вместе, а не отдельно. Третий был бы самым эффективным сервером, хотя, поскольку сервер будет делать все тяжелая работа.


после некоторого тестирования, unnest часто кажется очень быстрым вариантом, как я узнал из @Clodoaldo Neto ' s ответ на подобный вопрос.

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,))

однако может быть сложно с очень большими данными.


любой, кто использует SQLalchemy, может попробовать версию 1.2, которая добавила поддержку bulk insert для использования psycopg2.дополнительный.execute_batch () вместо executemany при инициализации движка с use_batch_mode=True как:

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

тогда кому-то придется использовать SQLalchmey не будет беспокоиться, чтобы попробовать различные комбинации sqla и psycopg2 и direct SQL вместе.


очень связанный вопрос:массовая вставка с SQLAlchemy ORM


все дороги ведут в Рим, но некоторые из них пересекает горы, требует паромов, но если вы хотите быстро просто выехать на трассу.


в этом случае автомагистраль должна использовать execute_batch() особенность psycopg2. В документации сказано это лучше всего:

текущая реализация executemany() is (используя чрезвычайно благотворительное преуменьшение) не особенно эффективно. Эти функции можно использовать для ускорения повторного выполнения инструкции по набору параметров. За счет уменьшения количества серверных раундтрипов производительность может быть на порядок лучше, чем при использовании executemany().

в моем собственном тесте execute_batch() и в два раза быстрее as executemany(), и дает возможность настроить page_size для дальнейшей настройки (если вы хотите сжать последние 2-3% производительности от драйвера).

та же функция может быть легко включена, если вы используете SQLAlchemy, установив use_batch_mode=True в качестве параметра при создании двигателя с create_engine()