Spec-Zone.ru › Django 1.8

Базовые транзакции базы данных

Django предоставляет несколько способов управления транзакциями базы данных.

Управление транзакциями базы данных

Поведение Django по умолчанию при работе с транзакциями

По умолчанию Django работает в режиме автоподтверждения. Каждый запрос сразу же подтверждается в базе данных, если нет активной транзакции. Подробности см. ниже.

Django автоматически использует транзакции или точки сохранения для обеспечения целостности операций ORM, требующих нескольких запросов, особенно запросов delete() и update().

Класс Django TestCase также оборачивает каждый тест в транзакцию по соображениям производительности.

Связывание транзакций с HTTP-запросами

Распространённый способ обработки транзакций в веб-приложениях — оборачивание каждого запроса в транзакцию. Установите ATOMIC_REQUESTS в значение True в конфигурации каждой базы данных, для которой вы хотите включить это поведение.

Это работает следующим образом. Перед вызовом функции представления Django запускает транзакцию. Если ответ получен без ошибок, Django подтверждает транзакцию. Если представление вызывает исключение, Django откатывает транзакцию.

Вы можете выполнять подтранзакции с помощью точек сохранения в вашем коде представления, как правило, с помощью менеджера контекста atomic(). Однако в конце представления все изменения либо будут подтверждены, либо нет.

Предупреждение

Хотя простота этой модели транзакций привлекательна, она также делает её неэффективной при увеличении трафика. Открытие транзакции для каждого представления несет определённые издержки. Влияние на производительность зависит от шаблонов запросов вашего приложения и от того, как ваша база данных обрабатывает блокировки.

Транзакции на уровне запроса и потоковые ответы

Когда представление возвращает StreamingHttpResponse, чтение содержимого ответа часто выполняет код для генерации содержимого. Поскольку представление уже вернуло результат, такой код выполняется вне транзакции.

Как правило, не рекомендуется записывать в базу данных во время генерации потокового ответа, так как нет разумного способа обработки ошибок после начала отправки ответа.

На практике эта функция просто оборачивает каждую функцию представления декоратором atomic(), описанным ниже.

Обратите внимание, что только выполнение вашего представления заключено в транзакции. Средства обработки находятся вне транзакции, так же как и отображение шаблонов.

Когда ATOMIC_REQUESTS включено, все еще возможно предотвратить выполнение представлений в транзакции.

non_atomic_requests(using=None) [source]

Этот декоратор аннулирует действие ATOMIC_REQUESTS для данного представления:

from django.db import transaction

@transaction.non_atomic_requests
def my_view(request):
    do_stuff()

@transaction.non_atomic_requests(using='other')
def my_other_view(request):
    do_stuff_on_the_other_database()

Он работает только при применении к самому представлению.

Явное управление транзакциями

Django предоставляет единый API для управления транзакциями базы данных.

atomic(using=None, savepoint=True) [source]

Атомарность — определяющее свойство баз данных. atomic позволяет нам создать блок кода, в котором гарантируется атомарность базы данных. Если блок кода успешно завершен, изменения подтверждаются в базе данных. Если возникает исключение, изменения отменяются.

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

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

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's default).
    do_stuff()

    with transaction.atomic():
        # This code executes inside a transaction.
        do_more_stuff()

Оборачивание atomic в блок try/except позволяет естественно обрабатывать ошибки целостности:

from django.db import IntegrityError, transaction

@transaction.atomic
def viewfunc(request):
    create_parent()

    try:
        with transaction.atomic():
            generate_relationships()
    except IntegrityError:
        handle_exception()

    add_children()

В этом примере, даже если generate_relationships() вызывает ошибку базы данных, нарушая ограничение целостности, вы можете выполнять запросы в add_children(), и изменения из create_parent() все еще будут там. Обратите внимание, что любые операции, выполненные в generate_relationships(), уже будут безопасно отменены, когда handle_exception() вызывается, поэтому обработчик исключений также может работать с базой данных при необходимости.

Избегайте перехвата исключений внутри atomic!

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

Это в основном касается DatabaseError и его подклассов, таких как IntegrityError. После такой ошибки транзакция прерывается, и Django выполнит откат в конце блока atomic . Если вы попытаетесь выполнить запросы к базе данных до того, как произойдет откат, Django поднимет TransactionManagementError. Вы также можете столкнуться с этим поведением, когда обработчик сигнала ORM вызывает исключение.

Правильный способ перехвата ошибок базы данных — вокруг блока atomic , как показано выше. При необходимости добавьте дополнительный блок atomic для этой цели. Этот шаблон имеет другое преимущество: он явно определяет, какие операции будут отменены, если произойдет исключение.

Если вы перехватываете исключения, вызванные сырыми SQL-запросами, поведение Django не определено и зависит от базы данных.

Для обеспечения атомарности atomic отключает некоторые API. Попытка подтвердить, отменить или изменить состояние автоматического подтверждения подключения к базе данных внутри блока atomic приведет к возникновению исключения.

atomic принимает аргумент using, который должен содержать имя базы данных. Если этот аргумент не указан, Django использует базу данных "default".

Под капотом код управления транзакциями Django:

  • открывает транзакцию при входе во внешний блок atomic;
  • создает точку сохранения при входе во внутренний блок atomic;
  • освобождает или откатывает к точке сохранения при выходе из внутреннего блока;
  • подтверждает или отменяет транзакцию при выходе из внешнего блока.

Вы можете отключить создание точек сохранения для внутренних блоков, задав аргумент savepoint в значение False. Если возникнет исключение, Django выполнит откат при выходе из первого родительского блока с точкой сохранения, если таковая имеется, и из внешнего блока в противном случае. Атомарность все еще гарантируется внешней транзакцией. Этот параметр следует использовать только в том случае, если накладные расходы на точки сохранения заметны. Его недостатком является нарушение описанной выше обработки ошибок.

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

Ранее внешний атомарный блок не мог быть объявлен с savepoint=False при отключенном автоматическом подтверждении.

Соображения по производительности

Открытые транзакции несут затраты на производительность для сервера базы данных. Чтобы минимизировать эти затраты, старайтесь держать транзакции как можно короче. Это особенно важно, если вы используете atomic() в долговременных процессах, вне цикла запроса/ответа Django.

Автоподтверждение

Зачем Django использует автоподтверждение

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

Это не всегда удобно для разработчиков приложений. Для решения этой проблемы большинство баз данных предоставляют режим автоподтверждения. При включенном автоподтверждении и отсутствии активной транзакции каждый SQL-запрос оборачивается в свою собственную транзакцию. Другими словами, каждый такой запрос не только запускает транзакцию, но и транзакция автоматически подтверждается или отменяется в зависимости от успеха запроса.

PEP 249, спецификация Python Database API v2.0, требует первоначального отключения автоподтверждения. Django переопределяет это значение по умолчанию и включает автоподтверждение.

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

Отключение управления транзакциями

Вы можете полностью отключить управление транзакциями Django для заданной базы данных, установив AUTOCOMMIT в False в её конфигурации. Если вы это сделаете, Django не включит автоподтверждение и не выполнит никаких подтверждений. Вы получите обычное поведение библиотеки подлежащей базы данных.

Это требует явного подтверждения каждой транзакции, даже тех, которые были инициированы Django или сторонними библиотеками. Таким образом, это лучше использовать в ситуациях, когда вы хотите запустить собственный транзакционный middleware или сделать что-то действительно странное.

API низкого уровня

Предупреждение

Всегда отдавайте предпочтение atomic(), если это возможно. Оно учитывает особенности каждой базы данных и предотвращает неверные операции.

API низкого уровня полезны только если вы реализуете собственное управление транзакциями.

Автоподтверждение

Django предоставляет простой API в модуле django.db.transaction для управления состоянием автоподтверждения каждого соединения с базой данных.

get_autocommit(using=None) [source]
set_autocommit(autocommit, using=None) [source]

Эти функции принимают аргумент using, который должен быть именем базы данных. Если он не предоставлен, Django использует базу данных "default".

Автоподтверждение изначально включено. Если вы его отключите, вам нужно будет его восстановить.

После отключения автоподтверждения вы получите поведение по умолчанию вашего адаптера базы данных, и Django вам не поможет. Хотя это поведение указано в PEP 249, реализации адаптеров не всегда согласованы друг с другом. Тщательно изучите документацию используемого вами адаптера.

Вы должны убедиться, что никакая транзакция не активна, обычно вызывая commit() или rollback(), прежде чем снова включить автоподтверждение.

Django откажется отключить автоподтверждение, когда активен блок atomic(), потому что это нарушит атомарность.

Транзакции

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

Django не предоставляет API для начала транзакции. Ожидаемый способ начать транзакцию — отключить автоподтверждение с помощью set_autocommit().

После начала транзакции вы можете применить произведённые изменения до этого момента с помощью commit() или отменить их с помощью rollback(). Эти функции определены в django.db.transaction.

commit(using=None) [source]
rollback(using=None) [source]

Эти функции принимают аргумент using, который должен быть именем базы данных. Если он не предоставлен, Django использует базу данных "default".

Django откажется от подтверждения или отката, когда активен блок atomic(), потому что это нарушит атомарность.

Точки сохранения

Точка сохранения — это маркер в транзакции, который позволяет отменить часть транзакции, а не всю транзакцию. Точки сохранения доступны с SQLite (≥ 3.6.8), PostgreSQL, Oracle и MySQL (при использовании движка хранения InnoDB). Другие бэкэнды предоставляют функции точек сохранения, но они являются пустыми операциями — фактически ничего не делают.

Точки сохранения не особенно полезны, если вы используете автоподтверждение, поведение Django по умолчанию. Однако, как только вы откроете транзакцию с помощью atomic(), вы создаете серию операций с базой данных, ожидающих подтверждения или отката. Если вы вызовете откат, вся транзакция будет отменена. Точки сохранения предоставляют возможность выполнить откаты более мелко, чем полный откат, который будет выполнен transaction.rollback().

Когда декоратор atomic() вложен, он создает точку сохранения, чтобы позволить частичное подтверждение или откат. Вам настоятельно рекомендуется использовать atomic(), а не описанные ниже функции, но они все еще являются частью публичного API и нет планов по их устареванию.

Каждая из этих функций принимает аргумент using, который должен быть именем базы данных, на которую распространяется поведение. Если аргумент using не предоставлен, используется база данных "default".

Точки сохранения контролируются тремя функциями в django.db.transaction:

savepoint(using=None) [source]

Создает новую точку сохранения. Это отмечает точку в транзакции, которая, как известно, находится в «хорошем» состоянии. Возвращает идентификатор точки сохранения (sid).

savepoint_commit(sid, using=None) [source]

Освобождает точку сохранения sid. Изменения, внесенные с момента создания точки сохранения, становятся частью транзакции.

savepoint_rollback(sid, using=None) [source]

Откатывает транзакцию до точки сохранения sid.

Эти функции ничего не делают, если точки сохранения не поддерживаются или если база данных находится в режиме автоподтверждения.

Кроме того, есть вспомогательная функция:

clean_savepoints(using=None) [source]

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

Следующий пример демонстрирует использование точек сохранения:

from django.db import transaction

# open a transaction
@transaction.atomic
def viewfunc(request):

    a.save()
    # transaction now contains a.save()

    sid = transaction.savepoint()

    b.save()
    # transaction now contains a.save() and b.save()

    if want_to_keep_b:
        transaction.savepoint_commit(sid)
        # open transaction still contains a.save() and b.save()
    else:
        transaction.savepoint_rollback(sid)
        # open transaction now contains only a.save()

Точки сохранения могут быть использованы для восстановления из ошибки базы данных, выполняя частичный откат. Если вы делаете это внутри блока atomic(), весь блок по-прежнему будет отменён, потому что он не знает, что вы обработали ситуацию на более низком уровне! Чтобы предотвратить это, вы можете управлять поведением отката с помощью следующих функций.

get_rollback(using=None) [source]
set_rollback(rollback, using=None) [source]

Установка флага отката в True принудительно вызывает откат при выходе из самого внутреннего блока atomic. Это может быть полезно для запуска отката без подъема исключения.

Установка его в False предотвращает такой откат. Прежде чем сделать это, убедитесь, что вы откатили транзакцию до известной хорошей точки сохранения внутри текущего атомарного блока! В противном случае вы нарушите атомарность, и может произойти повреждение данных.

Примечания для конкретных баз данных

Точки сохранения в SQLite

Хотя SQLite ≥ 3.6.8 поддерживает точки сохранения, упущение в проектировании модуля sqlite3 делает их почти неиспользуемыми.

Когда автоподтверждение включено, точки сохранения бессмысленны. Когда оно отключено, sqlite3 неявно подтверждает перед операторами savepoint. (Фактически, он подтверждает перед любым оператором, кроме SELECT, INSERT, UPDATE, DELETE и REPLACE.) Эта ошибка имеет два последствия:

  • API низкого уровня для контрольных точек доступны только внутри транзакции, т. е. внутри блока atomic().
  • Невозможно использовать atomic(), когда автоподтверждение отключено.

Транзакции в MySQL

Если вы используете MySQL, ваши таблицы могут или не могут поддерживать транзакции; это зависит от версии MySQL и типов используемых таблиц. (Под «типами таблиц» мы подразумеваем такие вещи, как «InnoDB» или «MyISAM».) Особенности транзакций MySQL выходят за рамки этой статьи, но на сайте MySQL есть информация о транзакциях MySQL.

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

Обработка исключений внутри транзакций PostgreSQL

Примечание

Этот раздел актуален только если вы реализуете собственное управление транзакциями. Эта проблема не может возникнуть в стандартном режиме Django, и atomic() обрабатывает её автоматически.

Внутри транзакции, когда вызов курсора PostgreSQL вызывает исключение (обычно IntegrityError), все последующие SQL-запросы в той же транзакции завершатся с ошибкой «текущая транзакция прервана, запросы игнорируются до конца блока транзакции». Хотя простое использование save() вряд ли вызовет исключение в PostgreSQL, существуют более сложные сценарии использования, которые могут это сделать, такие как сохранение объектов с уникальными полями, сохранение с флагом force_insert/force_update или вызов пользовательских SQL-запросов.

Существует несколько способов восстановиться после такого рода ошибок.

Откат транзакции

Первый вариант — откат всей транзакции. Например:

a.save() # Succeeds, but may be undone by transaction rollback
try:
    b.save() # Could throw exception
except IntegrityError:
    transaction.rollback()
c.save() # Succeeds, but a.save() may have been undone

Вызов transaction.rollback() откатывает всю транзакцию. Любые неподтверждённые операции с базой данных будут потеряны. В этом примере изменения, внесённые a.save() , будут потеряны, даже если эта операция сама не вызвала ошибку.

Откат контрольной точки

Вы можете использовать контрольные точки для управления объёмом отката. Перед выполнением операции с базой данных, которая может завершиться ошибкой, вы можете установить или обновить контрольную точку; таким образом, если операция завершится ошибкой, вы сможете откатить только проблемную операцию, а не всю транзакцию. Например:

a.save() # Succeeds, and never undone by savepoint rollback
sid = transaction.savepoint()
try:
    b.save() # Could throw exception
    transaction.savepoint_commit(sid)
except IntegrityError:
    transaction.savepoint_rollback(sid)
c.save() # Succeeds, and a.save() is never undone

В этом примере a.save() не будет отменён в случае, если b.save() вызовет исключение.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.8/topics/db/transactions/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API