Psycopg2, Postgresql, Python: быстрый способ массовой вставки - PullRequest
38 голосов
/ 16 февраля 2010

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

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

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

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

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

Ответы [ 8 ]

14 голосов
/ 16 февраля 2010

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

10 голосов
/ 16 февраля 2010

Существует новое руководство psycopg2 , содержащее примеры всех параметров.

Опция COPY является наиболее эффективной. Тогда исполнитель. Затем выполнить с pyformat.

8 голосов
/ 02 февраля 2014

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

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

6 голосов
/ 27 сентября 2012

Вы можете использовать новую библиотеку 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' }

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

1 голос
/ 02 января 2018

Любой, кто использует SQLalchemy, может попробовать версию 1.2, в которой добавлена ​​поддержка массовой вставки, чтобы использовать psycopg2.extras.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 и прямого SQL вместе.

1 голос
/ 31 декабря 2015

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

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

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

1 голос
/ 16 февраля 2010

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

0 голосов
/ 13 июня 2018

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


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


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

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

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

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

...