Транзакции и аномалии конкурентности в базах данных

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

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

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

В контексте управления параллелизмом в базах данных существует несколько широко используемых уровней изоляции, в частности, зафиксированное чтение (read committed), изоляция моментального снимка (snapshot isolation) (иногда называемая повторяющимся чтением (repeatable read)) и сериализуемый уровень (serializable). Эти уровни изоляции предотвращают различные состояния гонки (race conditions):

  • Грязные чтения (Dirty reads). Один клиент читает записи другого клиента до того, как они будут зафиксированы. Уровень изоляции зафиксированного чтения (read committed) и более строгие уровни предотвращают грязное чтение.
  • Грязные записи (Dirty writes). Один клиент перезаписывает данные, которые другой клиент написал, но еще не зафиксировал. Почти все реализации транзакций предотвращают грязную запись.
  • Отклонение чтения (Read skew) (неповторяющиеся чтения (nonrepeatable reads)). Клиент видит разные части базы данных в разные моменты времени. Эту проблему чаще всего предотвращают с помощью изоляции моментального снимка (snapshot isolation), которая позволяет транзакции читать из согласованного моментального снимка в определенный момент времени. Обычно это реализуется с помощью мультиверсионного управления параллелизмом (MVCC).
  • Потерянные обновления (Lost updates). Два клиента одновременно выполняют цикл чтения-изменения-записи. Один перезаписывает запись другого без внесения изменений, поэтому данные теряются. Некоторые реализации изоляции моментальных снимков (snapshot isolation) автоматически предотвращают эту аномалию, в то время как другие требуют ручной блокировки (SELECT FOR UPDATE).
  • Отклонение записи (Write skew). Транзакция что-то считывает, принимает решение на основе увиденного значения и записывает решение в базу данных. Однако к моменту записи предпосылка решения уже не соответствует действительности. Только сериализуемая изоляция (serializable isolation) предотвращает эту аномалию.
  • Фантомные чтения (Phantom reads). Транзакция считывает объекты, соответствующие некоторому условию поиска. Другой клиент делает запись, которая влияет на результаты этого поиска. Изоляция моментальных снимков (snapshot isolation) предотвращает прямое фантомное чтение, но фантомы в контексте перекоса записи (write skew) требуют специальной обработки, такой как блокировки диапазона индекса.

Слабые уровни изоляции защищают от некоторых из этих аномалий, но позволяют разработчику приложения обрабатывать другие вручную (например, с использованием явной блокировки). Только сериализуемая изоляция (serializable isolation) защищает от всех этих проблем. Существует три различных подхода к реализации сериализуемых транзакций:

  • Буквально выполнение транзакций в последовательном порядке. Если вы можете сделать каждую транзакцию очень быстрой для выполнения, а пропускная способность транзакции достаточно низкая для обработки на одном ядре CPU, это простой и эффективный вариант.
  • Двухфазная блокировка (Two-phase locking). На протяжении десятилетий это был стандартный способ реализации сериализуемости, но многие приложения избегают его использования из-за его характеристик производительности.
  • Изоляция сериализуемых моментальных снимков (SSI, Serializable snapshot isolation). Довольно новый алгоритм, позволяющий избежать большинства недостатков предыдущих подходов. Он использует оптимистичный подход, позволяя транзакциям проходить без блокировки. Когда транзакция хочет зафиксироваться, она проверяется и прерывается, если выполнение не было сериализуемым.

  • Читайте также:

Комментарии

Популярные сообщения из этого блога

Язык поисковых запросов в Graylog

Хэш-таблица: разрешение коллизий

Нормальные формы, пример нормализации в базе данных