Обеспечение бизнес-правил целостности данных при использовании уровня изоляции Read Committed крайне затруднительно, поскольку видимость данных меняется с каждым оператором, и даже выполнение одного оператора может выйти за рамки его снимка в случае конфликта записи.
В то время как транзакция с уровнем изоляции Repeatable Read обеспечивает стабильное представление данных на протяжении всего периода своего выполнения, существует определенная проблема при использовании MVCC снимков для проверки целостности данных, связанная с явлением, известным как конфликты чтения/записи. Если одна транзакция записывает данные, а параллельная транзакция пытается считать те же самые данные (до или после записи), она не видит изменений, внесенных другой транзакцией. Таким образом, транзакция чтения выглядит выполненной первой, независимо от фактического порядка запуска или фиксации данных транзакций. Если взаимодействие ограничивается этим, проблема не возникает, однако если читающая транзакция также записывает данные, которые считываются другой параллельной транзакцией, возникает ситуация, при которой одна транзакция кажется выполненной раньше обеих вышеупомянутых транзакций. Если транзакция, которая должна была выполниться последней, фактически фиксируется первой, в графе порядка выполнения транзакций может возникнуть цикл. При появлении такого цикла механизмы проверки целостности не смогут функционировать корректно без применения дополнительных средств.
Как упоминалось в Раздел 2.10.2.3, транзакции с уровнем изоляции Serializable представляют собой транзакции с уровнем изоляции Repeatable Read, в которые добавлен метод неблокирующего мониторинга опасных комбинаций конфликтов чтения/записи. При обнаружении комбинации, способной вызвать цикл в фактическом порядке выполнения, одна из вовлеченных транзакций откатывается для разрыва этого цикла.
Если уровень изоляции транзакций Serializable используется для всех операций записи и всех операций чтения, которым необходимо согласованное представление данных, никаких дополнительных усилий для обеспечения целостности не требуется. Программному обеспечению из других сред, написанному с расчетом на использование транзакций с уровнем изоляции Serializable для обеспечения согласованности данных, следует «функционировать корректно» в этом отношении в Digital Q.DataBase.
При использовании данного метода можно избежать излишней нагрузки на разработчиков, если прикладное программное обеспечение работает через фреймворк, который автоматически повторяет транзакции, откатываемые из-за ошибки сериализации. Целесообразно установить
default_transaction_isolation к serializable. Также целесообразно принять меры для обеспечения того, чтобы никакой другой уровень изоляции транзакций не использовался, будь то непреднамеренно или с целью обхода проверок целостности, путем проверки уровня изоляции транзакций в триггерах.
См. Раздел 2.10.2.3 для получения рекомендаций по производительности.
Данный уровень защиты целостности с использованием транзакций с уровнем изоляции Serializable пока не распространяется на режим горячего резерва (Раздел 3.11.4) или логические реплики. По этой причине при использовании горячего резерва или логической репликации может потребоваться уровень изоляции Repeatable Read и явное блокирование на основном сервере.
В случаях, когда возможна запись без использования уровня изоляции Serializable,
для обеспечения актуальности строки и её защиты от
одновременных обновлений необходимо использовать SELECT FOR UPDATE,
SELECT FOR SHARE, или соответствующий LOCK
TABLE оператор. (SELECT FOR UPDATE
и SELECT FOR SHARE блокируют от одновременных обновлений только возвращаемые строки, в то время как LOCK
TABLE блокирует всю таблицу.) Это следует учитывать при переносе приложений в
Digital Q.DataBase из других окружений.
Тем, кто переходит из других окружений, также следует обратить внимание на тот факт, что SELECT FOR UPDATE не гарантирует, что параллельная транзакция не обновит или не удалит выбранную строку. Чтобы сделать это в Digital Q.DataBase необходимо фактически обновить строку, даже если никакие значения менять не требуется.
SELECT FOR UPDATE временно блокирует
другие транзакции, препятствуя получению ими той же блокировки или выполнению
UPDATE или DELETE , которая повлияла бы на заблокированную строку, однако как только транзакция, удерживающая данную блокировку, будет зафиксирована или откачена, заблокированная транзакция продолжит выполнение конфликтующей операции, если только не было выполнено фактическое UPDATE строки в период удержания блокировки.
Проверки глобальной целостности требуют дополнительного анализа при использовании
уровней изоляции, отличных от уровня изоляции Serializable MVCC.
Например, банковскому приложению может потребоваться проверка того, что сумма
всех кредитов в одной таблице равна сумме дебетов в другой таблице,
когда обе таблицы активно обновляются. Сравнение результатов двух
последовательных SELECT sum(...) команды не будут работать надежно в режиме Read Committed, так как второй запрос, скорее всего, учтет результаты транзакций, которые не были учтены первым. Выполнение двух операций суммирования в рамках одной транзакции с уровнем изоляции Repeatable Read даст точную картину только тех результатов транзакций, которые были зафиксированы до начала данной транзакции с уровнем изоляции Repeatable Read — но можно закономерно задаться вопросом, сохранит ли ответ свою актуальность к моменту его получения. Если транзакция с уровнем изоляции Repeatable Read сама внесла некоторые изменения перед выполнением проверки согласованности, полезность такой проверки становится еще более спорной, поскольку теперь она учитывает лишь часть изменений, внесенных после начала транзакции. В таких случаях
в целях предосторожности может потребоваться блокировка всех необходимых для проверки таблиц,
чтобы получить неоспоримую картину текущего состояния данных. A
SHARE блокировка в данном режиме (или более высоком) гарантирует отсутствие в заблокированной таблице любых незафиксированных изменений, кроме изменений, внесенных текущей транзакцией.
Следует также отметить, что если для предотвращения одновременных изменений используется явное блокирование, необходимо либо использовать режим Read Committed, либо в режиме Repeatable Read заблаговременно получать блокировки до выполнения запросов. Блокировка, полученная транзакцией с уровнем изоляции Repeatable Read, гарантирует, что никакие другие транзакции, изменяющие таблицу, более не выполняются; однако если снимок, используемый транзакцией, был сформирован до получения блокировки, он может не учитывать некоторые уже зафиксированные изменения в таблице. Снимок транзакции с уровнем изоляции Repeatable Read фактически фиксируется в момент начала её первого запроса или команды изменения данных (SELECT, INSERT,
UPDATE, DELETE, или
MERGE), поэтому существует возможность получить блокировки явным образом
до того, как снимок будет зафиксирован.