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