Агрегирование save() s в Django?

Я использую Django с бэкэнд sqlite, и проблема с записью - проблема. На каком-то этапе я могу закончить "правильный" db, но на данный момент я застрял в sqlite. Я думаю, что мои проблемы с производительностью записи, вероятно, связаны с тем, что я создаю большое количество строк, и, предположительно, каждый раз, когда я save(), он блокирует, разблокирует и синхронизирует БД на диске.

Как я могу объединить большое количество вызовов save() в одну операцию базы данных?

Ответ 1

РЕДАКТИРОВАНИЕ: commit_on_success устарела и была удалена в Django 1.8. Вместо этого используйте transaction.atomic. Смотри ответ Фрейзера Харриса.

На самом деле это легче сделать, чем вы думаете. Вы можете использовать транзакции в Django. Эти пакетные операции с базой данных (в частности, сохранение, вставка и удаление) в одну операцию. Я нашел самый простой в использовании - commit_on_success. По сути, вы оборачиваете операции сохранения базы данных в функцию, а затем используете декоратор commit_on_success.

from django.db.transaction import commit_on_success

@commit_on_success
def lot_of_saves(queryset):
    for item in queryset:
        modify_item(item)
        item.save()

Это будет иметь огромное увеличение скорости. Вы также получите преимущество откатов, если какой-либо из элементов выйдет из строя. Если у вас есть миллионы операций сохранения, вам, возможно, придется фиксировать их в блоках, используя commit_manually и transaction.commit() но мне это редко понадобилось.

Надеюсь, это поможет,

Будет

Ответ 2

Новый по Django 1.6 атомный, простой API для управления транзакциями БД. Скопировано дословно из документов:

атом можно использовать как decorator:

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

Наследие django.db.transaction функции autocommit(), commit_on_success() и commit_manually() устарели и будут удалены в Django 1.8.

Ответ 3

Я думаю, что это метод, который вы ищете: https://docs.djangoproject.com/en/dev/ref/models/querysets/#bulk-create

Код скопирован из документации:

Entry.objects.bulk_create([
    Entry(headline='This is a test'),
    Entry(headline='This is only a test'),
])

Который на практике будет выглядеть так:

my_entries = list()
for i in range(100):
    my_entries.append(Entry(headline='Headline #'+str(i))

Entry.objects.bulk_create(my_entries)

Согласно документам, это выполняет один запрос, независимо от размера списка (максимум 999 элементов в SQLite3), чего нельзя сказать о atomic декораторе.

Есть важное различие, которое нужно сделать. По OP-вопросу звучит так, что он пытается создать массовое создание, а не массовое сохранение. atomic декоратор - самое быстрое решение для сохранения, но не для создания.

Ответ 4

"Как я могу объединить большое количество вызовов save() в одну операцию с базой данных?"

Вам не нужно. Django уже управляет кешем для вас. Вы не можете улучшить его кэширование DB, пытаясь суетиться с сохранением.

"Проблемы с производительностью записи, вероятно, связаны с тем, что я создаю большое количество строк" ​​

Правильно.

SQLite довольно медленный. Так оно и есть. Запросы быстрее, чем у большинства других БД. Записи довольно медленные.

Рассмотрим более серьезные изменения архитектуры. Вы загружаете строки во время веб-транзакции (т.е. Массовая загрузка файлов и загрузка БД из этих файлов)?

Если вы выполняете массовую загрузку внутри веб-транзакции, остановитесь. Вам нужно сделать что-то умнее. Используйте celery или используйте какой-либо другой "пакетный" способ, чтобы делать ваши нагрузки в фоновом режиме.

Мы пытаемся ограничить себя проверкой файлов в веб-транзакции и выполнять нагрузки, когда пользователь не ждет их страницы HTML.