Разберите последствие: почему при сериализуемом уровне изоляции транзакция может быть прервана ошибкой сериализации, хотя две транзакции не изменяли одну и ту же строку?
При уровне SERIALIZABLE СУБД должна гарантировать результат, эквивалентный некоторому последовательному выполнению транзакций. Поэтому она отслеживает не только конфликты записи одной строки, но и зависимости чтения-записи между транзакциями. Если обнаружена потенциальная циклическая зависимость, СУБД может прервать одну транзакцию с ошибкой сериализации, даже без прямого конфликта обновления строк.
Такое завершение не означает повреждение данных: откат одной транзакции разрывает опасную зависимость. Обычно приложение должно повторить всю транзакцию.
Наивная реализация SERIALIZABLE через строгие блокировки обеспечивает корректность, но может надолго блокировать чтения и снижать конкурентность. Подходы на основе снимков данных уменьшили блокировки чтения, однако сами по себе допускают некоторые аномалии конкурентного выполнения.
Serializable Snapshot Isolation и близкие механизмы появились как компромисс: сохранить сериализуемый результат, выполняя многие операции чтения без блокировок. Цена этого подхода — возможное прерывание транзакции при обнаружении опасной зависимости.
Рассмотрим две транзакции: первая читает данные, зависящие от строки X, и изменяет строку Y; вторая читает данные, зависящие от строки Y, и изменяет строку X. Они не пытаются одновременно изменить одну строку, поэтому конфликта записи-записи может не быть.
Однако результат каждой транзакции зависит от состояния, которое потенциально изменяет другая. Если обе транзакции будут зафиксированы, их совместный результат может быть невозможен при любом последовательном порядке. При менее строгой изоляции это иногда допускается, а при SERIALIZABLE должно быть предотвращено.
СУБД может представлять зависимости между транзакциями как направленные связи. Связь чтение-запись возникает, когда одна транзакция читает версию или предикат, а другая изменяет соответствующие данные; опасной становится комбинация таких связей, образующая цикл или структуру, которая может привести к циклу сериализации.
При обнаружении такой структуры реализация может выбрать одну транзакцию для отката. Это не обязательно транзакция, которая первой начала работу или изменила больше строк: конкретный выбор зависит от алгоритма и состояния системы. В других реализациях сериализуемость может обеспечиваться преимущественно блокированием, поэтому поведение при конфликте будет отличаться.
Важно различать прямой конфликт строк и конфликт сериализации. Первый обычно виден по конкурирующим блокировкам или версиям одной записи. Второй может возникнуть через предикатное чтение, диапазон строк или цепочку зависимостей между разными строками.
Уровень SERIALIZABLE не гарантирует, что каждая транзакция обязательно завершится успехом. Он гарантирует корректность успешно зафиксированного совместного результата, а не отсутствие отказов. Приложение должно уметь распознавать ошибку сериализации, откатывать неудачную транзакцию и повторять её целиком с ограничением числа попыток.
Компромисс зависит от нагрузки. Блокирующая реализация чаще увеличивает ожидание, а SSI-подобная реализация чаще сохраняет параллельность чтений, но может увеличивать число откатов при высокой конфликтности. Ослабление изоляции уменьшает эти издержки, но возвращает риск аномалий.
В отчётной системе много длительных чтений, а параллельные операции обновляют связанные, но разные записи. Вариант со строгими блокировками обеспечивает корректность, но отчёты начинают ждать завершения операций записи. Вариант с более слабой изоляцией устраняет ожидание, однако допускает результаты, которые нельзя объяснить последовательным порядком.
Выбран сериализуемый режим на основе контроля зависимостей. Чтения обычно не блокируют записи, а при редком опасном пересечении одна транзакция получает ошибку сериализации и повторяется на уровне сервиса. Такой вариант оправдан, если транзакции короткие, повторяемые, а количество конфликтов ниже стоимости постоянных блокировок.
Если повторные откаты становятся частыми, систему оптимизируют: сокращают длительность транзакций, уменьшают область чтения, упорядочивают операции и анализируют, действительно ли всем операциям нужен SERIALIZABLE. Простое бесконечное повторение опасно: оно может усилить нагрузку и увеличить очередь конфликтующих транзакций.
Нет. Одна зависимость сама по себе не доказывает нарушение сериализуемости. Обычно нужен цикл или распознаваемая опасная структура зависимостей. Некоторые алгоритмы используют консервативные проверки и могут прервать транзакцию до полного доказательства нарушения, поэтому возможны ложные срабатывания.
Нет. Явная блокировка защищает выбранные строки, но не обязательно покрывает диапазон, предикат или другую запись, через которую проходит зависимость. Кроме того, блокировка может изменить порядок ожидания, но не заменяет анализ всех логических зависимостей. Для гарантии результата нужно защищать именно бизнес-инвариант, а не случайный набор строк.
Потому что конфликт может стать очевидным только при проверке зависимостей ближе к фиксации или во время контроля сериализуемости. Отдельные операторы могли завершиться успешно, но итог всей транзакции оказался несовместимым с последовательным порядком. Поэтому окончательным результатом считается только успешная фиксация всей транзакции; ошибка сериализации требует отката и повторного выполнения транзакции целиком.