13.4. Проверка согласованности данных на уровне приложения#

13.4. Проверка согласованности данных на уровне приложения

13.4. Проверка согласованности данных на уровне приложения

Очень сложно обеспечить соблюдение бизнес-правил, касающихся целостности данных, при использовании транзакций с уровнем изоляции Read Committed, поскольку представление данных меняется с каждым оператором, и даже один оператор может не ограничиваться снимком оператора, если возникает конфликт записи.

В то время как транзакция с повторяемым чтением имеет стабильное представление данных на протяжении всего своего выполнения, существует незаметная проблема при использовании снимков MVCC для проверки согласованности данных, связанная с так называемыми конфликтами чтения/записи. Если одна транзакция записывает данные, а параллельная транзакция пытается прочитать те же данные (независимо от того, до или после записи), она не может увидеть работу другой транзакции. Читатель затем кажется, что он выполнился первым, независимо от того, какая транзакция началась или зафиксировалась первой. Если на этом все заканчивается, проблемы нет, но если читатель также записывает данные, которые читаются параллельной транзакцией, теперь есть транзакция, которая кажется выполнилась раньше любой из ранее упомянутых транзакций. Если транзакция, которая кажется выполнилась последней, фактически фиксируется первой, очень легко появится цикл в графе порядка выполнения транзакций. Когда такой цикл появляется, проверки целостности не будут работать правильно без некоторой помощи.

Как упоминается в Раздел 13.2.3, сериализуемые транзакции являются просто транзакциями с повторяемым чтением, которые добавляют неблокирующий мониторинг для опасных ситуаций конфликтов чтения/записи. Когда обнаруживается ситуация, которая может вызвать цикл в видимом порядке выполнения, одна из участвующих транзакций откатывается, чтобы разорвать цикл.

13.4.1. Соблюдение согласованности с помощью сериализуемых транзакций

Если для всех записей и для всех чтений, которые требуют последовательного представления данных, используется уровень изоляции транзакций Serializable, то не требуется никаких дополнительных усилий для обеспечения согласованности. Программное обеспечение из других сред, которое написано для использования последовательных транзакций для обеспечения согласованности, должно просто работать в этом отношении в Tantor SE.

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

См. Раздел 13.2.3 для рекомендаций по производительности.

Предупреждение

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

13.4.2. Соблюдение согласованности с явными блокировками

Когда возможны непоследовательные записи, чтобы обеспечить текущую допустимость строки и защитить ее от одновременных обновлений, необходимо использовать команды SELECT FOR UPDATE, SELECT FOR SHARE или соответствующую команду LOCK TABLE. (SELECT FOR UPDATE и SELECT FOR SHARE блокируют только возвращенные строки от одновременных обновлений, в то время как команда LOCK TABLE блокирует всю таблицу). Это следует учитывать при портировании приложений в Tantor SE из других сред.

Также следует отметить для тех, кто переходит из других сред, что команда SELECT FOR UPDATE не гарантирует, что параллельная транзакция не обновит или удалит выбранную строку. Чтобы сделать это в Tantor SE, необходимо фактически обновить строку, даже если значения не требуется менять. SELECT FOR UPDATE временно блокирует другие транзакции от получения той же самой блокировки или выполнения UPDATE или DELETE, которые затронут заблокированную строку, но как только транзакция, удерживающая эту блокировку, коммитится или откатывается, заблокированная транзакция продолжит конфликтующую операцию, если фактическое обновление (UPDATE) строки было выполнено во время удержания блокировки.

Глобальные проверки корректности требуют дополнительного внимания в случае непоследовательной MVCC. Например, банковское приложение может желать проверить, что сумма всех кредитов в одной таблице равна сумме дебетов в другой таблице, когда обе таблицы активно обновляются. Сравнение результатов двух последовательных команд SELECT sum(...) не будет работать надежно в режиме Read Committed, так как второй запрос, скорее всего, будет включать результаты транзакций, не учтенных первым запросом. Выполнение двух сумм в рамках одной повторяемой транзакции чтения даст точную картину только о влиянии транзакций, завершившихся до начала повторяемой транзакции чтения - но можно справедливо задаться вопросом, актуальны ли ответы к моменту их получения. Если повторяемая транзакция чтения сама применяет некоторые изменения перед попыткой выполнить проверку согласованности, полезность проверки становится еще более спорной, так как теперь она включает некоторые, но не все изменения, произошедшие после начала транзакции. В таких случаях осторожный человек может захотеть заблокировать все таблицы, необходимые для проверки, чтобы получить неопровержимую картину текущей реальности. Режим блокировки SHARE (или выше) гарантирует, что в заблокированной таблице нет неподтвержденных изменений, кроме изменений текущей транзакции.

Обратите внимание, что если вы полагаетесь на явную блокировку для предотвращения одновременных изменений, вы должны либо использовать режим Read Committed, либо в режиме Repeatable Read быть осторожными и получать блокировки до выполнения запросов. Блокировка, полученная транзакцией с повторяемым чтением, гарантирует, что никакие другие транзакции, изменяющие таблицу, не выполняются, но если снимок, видимый транзакцией, предшествует получению блокировки, он может предшествовать некоторым теперь подтвержденным изменениям в таблице. Снимок транзакции с повторяемым чтением фактически замораживается в начале ее первого запроса или команды модификации данных (SELECT, INSERT, UPDATE, DELETE или MERGE), поэтому возможно явное получение блокировок до замораживания снимка.