Основы реляционных баз данных

Транзакционность

Далеко не все операции с базой данных можно выразить одним запросом. Классический пример — перевод денег с одного счёта на другой. Допустим, у нас есть таблица счетов accounts, в которой две записи:

id user_id amount
1 10 100
2 30 100

Процесс перевода можно грубо (потому что в реальности всё гораздо сложнее) представить так:

  1. Получаем количество денег пользователя.

    SELECT amount FROM accounts WHERE user_id = 10;
    
  2. Списываем необходимую сумму со счёта этого пользователя.

    UPDATE accounts SET amount = amount - 50 WHERE user_id = 10;
    
  3. Зачисляем деньги другому пользователю:

    UPDATE accounts SET amount = amount + 50 WHERE user_id = 30;
    

В результате этих манипуляций таблица примет следующий вид:

id user_id amount
1 10 50
2 30 150

Главная (но не единственная) проблема в этом процессе — отсутствие гарантии завершения. Представьте, что система успела выполнить списание, и в этот момент произошла ошибка, выключили питание, компьютер перезагрузился. В результате получится странная ситуация: деньги списались, но никуда не зачислились:

id user_id amount
1 10 50
2 30 100

Подобное поведение недопустимо не только при работе с деньгами, но и в большинстве других ситуаций. Данные приложения, по возможности (в распределённых системах это невозможно, см. CAP-теорему и Eventual Consistency), должны находиться в согласованном состоянии. И добиться этого можно с помощью механизма транзакций. Понятие "транзакция" не является специфичным для баз данных, им пользуются и в обычной жизни. Например, операция снятия денег в банкомате — это бизнес-транзакция. Мы, как пользователи банкомата, ожидаем, что эта операция либо снимет деньги, либо нет, и банкомат это обеспечивает. Но, если копнуть глубже, то станет понятно, что операция снятия денег — это нетривиальный процесс, который приводит не только к множеству запросов в базу данных, но и к затрагиванию нескольких (многих?) систем, у которых свои собственные процессы и базы данных внутри.

Как бы там ни было, главное, что мы ожидаем от любой подобной транзакции — атомарность. С точки зрения пользователя она всегда выглядит, как одна операция, которая либо завершается успешно либо не проходит. Транзакции в базе данных, в этом смысле, значительно проще, чем бизнес-транзакции. За обеспечением необходимых гарантий следит сама СУБД, а не программист:

BEGIN;
SELECT amount FROM accounts WHERE user_id = 10;
UPDATE accounts SET amount = amount - 50 WHERE user_id = 10;
UPDATE accounts SET amount = amount + 50 WHERE user_id = 30;
COMMIT;

Транзакции в PostgreSQL — это блок запросов, обрамлённый запросами BEGIN и COMMIT. Первый запрос открывает транзакцию, второй — её закрывает. Любая ошибка, возникшая внутри транзакции, откатывает целиком все изменения, которые были сделаны после запроса BEGIN. При необходимости, транзакцию можно откатить самостоятельно, выполнив запрос ROLLBACK до COMMIT. Подобное иногда бывает нужно при выполнении запросов из кода приложения.

Обработка транзакций

Согласованность — одно из 4-x требований, которые предъявляются к любым транзакциям. В информатике акроним ACID описывает требования к транзакционной системе (не обязательно базе данных), обеспечивающие наиболее надёжную и предсказуемую её работу. Требования ACID были в основном сформулированы в конце 70-х годов:

Atomicity — Атомарность

Любая транзакция не может быть частично завершена — она либо выполнена, либо нет.

Consistency — Согласованность

Завершившаяся транзакция должна сохранять согласованность базы данных. Другими словами, каждая успешная транзакция по определению фиксирует только допустимые результаты, при том, что в процессе работы транзакции данные могут оказываться не согласованными. В примере выше снятие денег с одного счёта приводит к тому, что данные рассинхронизированы, но в момент завершения транзакции этого нет. Гарантия согласованности данных не может быть полностью обеспечена только средствами базы данных (например, различные констрейнты). Поддержка этого свойства включает в себя работу со стороны программистов, которые пишут необходимый для этого код.

Isolation — Изолированность

Во время выполнения транзакции параллельные транзакции не должны оказывать влияния на её результат. Ни одна транзакция не может увидеть изменения, сделанные другими незавершёнными транзакциями. Изолированность — требование дорогое, поэтому в реальных БД существуют режимы, не полностью изолирующие транзакцию (уровни изолированности Repeatable Read и ниже).

Durability — Устойчивость

Независимо от проблем на нижних уровнях (к примеру, обесточивание системы или сбои в оборудовании) изменения, сделанные успешно завершённой транзакцией, должны остаться сохранёнными после возвращения системы в работу. Другими словами, если пользователь получил подтверждение от системы, что транзакция выполнена, он может быть уверен, что сделанные им изменения не будут отменены из-за какого-либо сбоя.


Дополнительные материалы

  1. Официальная документация
  2. ACID
  3. Уровни изоляции транзакции

<span class="translation_missing" title="translation missing: ru.web.courses.lessons.mentors.mentor_avatars">Mentor Avatars</span>

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

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

Для полного доступа к курсу нужна профессиональная подписка

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

Получить доступ
115
курсов
892
упражнения
2241
час теории
3196
тестов

Открыть доступ

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

  • 115 курсов, 2000+ часов теории
  • 800 практических заданий в браузере
  • 250 000 студентов

Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг».

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Rambler
Логотип компании Bookmate
Логотип компании Botmother

Есть вопрос или хотите участвовать в обсуждении?

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг».