Каков эффективный способ вставки тысяч записей в таблицу SQLite с использованием Django?

61

Мне нужно вставить 8000+ записей в базу данных SQLite с помощью Django ORM. Эта операция должна выполняться как cronjob примерно раз в минуту.
На данный момент я использую цикл for для повторения всех элементов, а затем вставляю их один за другим.
Пример:

for item in items:
    entry = Entry(a1=item.a1, a2=item.a2)
    entry.save()

Каков эффективный способ сделать это?

Изменить: Небольшое сравнение между двумя способами вставки.

Без commit_manually decorator (11245 записей):

nox@noxdevel marinetraffic]$ time python manage.py insrec             

real    1m50.288s
user    0m6.710s
sys     0m23.445s

Использование commit_manual decorator (11245 записей):

[nox@noxdevel marinetraffic]$ time python manage.py insrec                

real    0m18.464s
user    0m5.433s
sys     0m10.163s

Примечание. Тест script также выполняет некоторые другие операции, кроме вставки в базу данных (загружает ZIP файл, извлекает XML файл из ZIP-архива, анализирует XML файл), поэтому время необходимый для выполнения, не обязательно означает время, необходимое для вставки записей.

Теги:
insert

8 ответов

112
Лучший ответ

Вы хотите проверить django.db.transaction.commit_manually.

http://docs.djangoproject.com/en/dev/topics/db/transactions/#django-db-transaction-commit-manually

Итак, это будет что-то вроде:

from django.db import transaction

@transaction.commit_manually
def viewfunc(request):
    ...
    for item in items:
        entry = Entry(a1=item.a1, a2=item.a2)
        entry.save()
    transaction.commit()

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

В django 1.3 были введены контекстные менеджеры. Итак, теперь вы можете использовать transaction.commit_on_success():

from django.db import transaction

def viewfunc(request):
    ...
    with transaction.commit_on_success():
        for item in items:
            entry = Entry(a1=item.a1, a2=item.a2)
            entry.save()

В django 1.4 добавлен bulk_create, что позволяет создавать списки ваших объектов модели и затем совершать их все сразу.

ПРИМЕЧАНИЕ метод сохранения не будет вызываться при использовании массового создания.

>>> Entry.objects.bulk_create([
...     Entry(headline="Django 1.0 Released"),
...     Entry(headline="Django 1.1 Announced"),
...     Entry(headline="Breaking: Django is awesome")
... ])

В django 1.6 был введен transaction.atomic, предназначенный для замены ныне устаревших функций commit_on_success и commit_manually.

из документации django для атома:

атом можно использовать как в качестве декоратора:

from django.db import transaction

@transaction.atomic
def viewfunc(request):
    # This code executes inside a transaction.
    do_stuff()

и как менеджер контекста:

from django.db import transaction

def viewfunc(request):
    # This code executes in autocommit mode (Django default).
    do_stuff()

    with transaction.atomic():
        # This code executes inside a transaction.
        do_more_stuff()
  • 7
    Это создаст их экземпляры как модели и запустит тысячи отдельных вставок. Мне всегда приходилось переходить на SQL и делать ручные пакетные вставки для такого типа тома; Джанго не создан для этого. Но да, вы определенно хотите одну транзакцию, если вы делаете это таким образом.
  • 0
    Я не очень хорошо знаю Django ORM, но разве ORM не генерирует SQL для вас? А в простой модели без внешних ключей разве один экземпляр не переводится в один оператор вставки?
Показать ещё 6 комментариев
11

Массовое создание доступно в Django 1.4:

https://django.readthedocs.io/en/1.4/ref/models/querysets.html#bulk-create

  • 0
    это работает так же, как работает сохранение? например, сохранить или обновить каждый объект?
3

Возможно, вам лучше загружать детали - подготовьте файл и используйте инструмент массовой загрузки. Это будет значительно более эффективным, чем 8000 отдельных вставок.

3

Посмотрите этот. Это предназначено для использования готовых продуктов только с MySQL, но есть указатели на то, что делать для других баз данных.

2

Чтобы ответить на вопрос, в частности, в отношении SQLite, как я уже сказал, хотя я только что подтвердил, что bulk_create действительно обеспечивает огромное ускорение, существует ограничение с SQLite: "По умолчанию создается все объекты в одной партии, за исключением SQLite, где значение по умолчанию таково, что используется максимум 999 переменных для каждого запроса."

Цитата из документа - A-IV предоставила ссылку.

Что я должен добавить, так это то, что этот djangosnippets запись alpar также работает для меня. Это небольшая оболочка, которая разбивает большую партию, которую вы хотите обработать на более мелкие партии, управляя пределом переменных 999.

2

Вы должны проверить DSE. Я написал DSE для решения таких проблем (массивная вставка или обновления). Использование django orm является тупиковым, вы должны сделать это в простом SQL, и DSE позаботится о многом для вас.

Томас

  • 1
    Еще одна вещь; Если вы решили использовать обычный SQL и если вставляемый SQL каждый раз содержит одни и те же поля, попробуйте использовать cursor.executemany (SQL, [список записей для вставки]). Гораздо быстрее, чем запускать вставку для каждой записи.
0

Я рекомендую использовать простой SQL (не ORM), вы можете вставить несколько строк с одной вставкой:

insert into A select from B;

Часть выберите из B часть вашего sql может быть такой же сложной, как вы хотите, чтобы она дошла до того, как результаты совпали с столбцами в таблице A, и нет конфликтов ограничений.

-4

Я столкнулся с одной и той же проблемой, и я не могу найти способ сделать это без большого количества вставок. Я согласен с тем, что использование транзакций, вероятно, является правильным способом его решения, но вот мой взлом:

 def viewfunc(request):
     ...
     to_save = [];
     for item in items:
         entry = Entry(a1=item.a1, a2=item.a2)
         to_save.append(entry);
     map(lambda x: x.save(), to_save);

Ещё вопросы

Сообщество Overcoder
Наверх
Меню