Базы данных Oracle - статьи

         

Проблема моды изоляции чтения.


В действительности эта проблема существует, и понятно, откуда она берется: необходимо как-то учесть эффекты, связанные с тем, что пока один пользователь читает данные, другой пользователь (или другие пользователи) может эти данные изменять. Стандарт ANSI SQL-92 описывает требования к реализации нескольких т.н. мод изоляции операций чтения от выполняющихся одновременно с ним транзакций. Они варьируют от самой “слабой” моды - т.н. “незафиксированного” (часто называемого “грязным”) чтения, при котором допускается считывание данных незафиксированных транзакций, до самой “сильной” - т.н. “повторяемого” чтения, при которой гарантируется повторяемость результата при повторении операции в рамках транзакции* .Беда вся в том, что само наличие всех этих различных мод изоляции в стандарте SQL отражает отнюдь не потребности пользователей (трудно представить себе, например, разработчика приложения, заинтересованного в чтении данных чужих незафиксированных транзакций - если только он не страдает такой особой формой мазохизма), а различные степени компромисса с возможностями разработчиков СУБД. Пользователей же волнует (или во всяком случае должно волновать) совсем другое: как избежать тех неприятных эффектов, которые могут быть связаны с использованием всех стандартных мод изоляции, кроме самой “сильной” из них. Чтобы не быть голословным, рассмотрим очень простой пример. Допустим, в некоей банковской системе есть таблица, состоящая всего из двух полей:

СЧЕТА



Номер_счета

Сумма

1

100000

2

100000

3

100000

.

.

.

.

.

.

99,998

100000

99,999

100000

100,000

100000

Предположим, пользователь запустил операцию подсчета общей суммы денег на всех счетах:

select sum(Сумма) from СЧЕТА;

В любой СУБД такая операция выполняется путем последовательного перебора всех записей таблицы с подсчетом требуемой суммы. Пусть теперь другой пользователь одновременно с выполнением этого отчета (то есть после его начала, но до его завершения) проводит простую транзакцию, переводящую 50000 с одного счета на другой:

update СЧЕТА set Сумма = Сумма - 50000 where Номер_счета = 1; update СЧЕТА set Сумма = Сумма + 50000 where Номер_счета = 100000; commit;


Нетрудно заметить, что данная транзакция не изменяет общей суммы на счетах, но если отчет выполняется в любой стандартной моде изоляции, отличной от “повторяемого” чтения, он неизбежно выдаст результат на 50000 больший! Причина в том, что с первого счета будет считана старая сумма, с последнего же - новая. Если при этом используется устанавливаемая по умолчанию в большинстве СУБД мода изоляции “завершенное чтение” (гарантирующая считывание данных только завершенных транзакций), то это только усугубит ситуацию: даже если транзакция не успеет завершиться до конца выполнения отчета, последний попросту остановится и будет ждать ее завершения, поскольку реализуется данная мода именно с помощью такой блокировки. Наличие указанных блокировок может привести и к еще более неприятным последствиям, если подключить третьего пользователя: нетрудно изменить пример так, что система попросту войдет в “клинч”: пользователи окажутся в состоянии взаимного ожидания.

Если все моды изоляции, кроме “повторяемого чтения”, столь опасны, то спрашивается: зачем они вообще допускаются? Дело в том, что при традиционном подходе реализация всех этих мод изоляции достигается с помощью блокировок того или иного уровня, причем чем “сильнее” мода изоляции, тем обременительнее блокировки: не блокирующим является лишь “грязное” чтение, а в случае “повторяемого чтения” на время выполнения отчета блокируются все записи, которые им могут быть считаны - т.е. фактически СУБД может почти потерять свои многопользовательские свойства!


Содержание раздела