|
Программирование >> Oracle
При использовании многовариантного доступа в Oracle получается ответ, согласованный на момент начала выполнения запроса. В других СУБД при использовании разделяемых блокировок чтения получается результат, согласованный на момент завершения запроса - на момент, когда вообще можно получить результат (подробнее об этом - ниже). В системе, использующей для обеспечения уровня изолированности REPEATABLE READ разделяемые блокировки чтения, строки будут блокироваться по мере их обработки запросом. Поэтому, возвращаясь к рассматривавшемуся выше примеру, по мере чтения запросом таблицы счетов, он будет устанавливать разделяемые блокировки чтения на каждой строке: Время Т4 Т5 Т6 Т7 Запрос Читает строку 1, sum = 500 $. На блоке 1 установлена разделяемая блокировка. Читает строку 2, sum = 740,25 На блоке 2 установлена разделяемая блокировка. Транзакция по переводу со счета на счет Читает строку N, sum Фиксирует транзакцию. Пытается изменить строку 1, но эта попытка блокируется. Транзакция приостанавливается, пока не сможет установить исключительную блокировку. Читает строку 342023, находит баланс счета, 100 $, и дает окончательный результат. Изменяет строку 1, устанавливая на соответствующий блок исключительную блокировку. Теперь баланс в этой строке имеет значение 100 $. Изменят строку 342023, устанавливая на соответствующий блок исключительную блокировку. Теперь баланс в этой строке имеет значение 500 $. Транзакция фиксируется. Эта таблица показывает, что корректный результат получен благодаря физическому упорядочению двух транзакций. Вот один из побочных эффектов использования разделяемых блокировок чтения для получения согласованных результатов: сеансы счит1ва-ния данн1х блокируют сеансы записи данн1х. Кроме того, в этих системах сеансы записи данных блокируют сеансы их считывания. Итак, понятно, как разделяемые блокировки чтения снижают параллелизм, но они также могут приводить к возникновению спорадических ошибок. В следующем примере мы начнем с исходной таблицы счетов, но в этот раз целью будет перевод 50,00 $ со счета 987 на счет 123. Время Запрос Т1 Читает строку 1, sum = 500 $. На строке 1 установлена разделяемая блокировка. Т2 Читает строку 2, sum = 740,25 $. На строке 2 установлена разделяемая блокировка, Транзакция по переводу со счета на счет Изменяет строку 342023, устанавливает исключительную блокировку на строку 342023, предотвращающую другие изменения и установку разделяемых блокировок. В этой строке теперь содержится значение 50 $. Читает строку N, sum = Пытается изменить строку 1, но она заблокирована. Транзакция приостанав ливается до тех пор, пока не появится возможность установить исключительную блокировку. Т6 Пытается прочитать строку 342023, но не может, поскольку на нее уже установлена исключительная блокировка. Только что мы получили классическую ситуацию взаимной блокировки. Наш запрос удерживает ресурс, необходимый транзакции, изменяющей данные, и наоборот. Запрос и изменяющая транзакция взаимно заблокировали друг друга. Один из сеансов будет выбран в качестве жертвы, и его транзакция будет прекращена. Затрачено немало времени и ресурсов и все впустую: произошел откат. Это - второй побочный эффект разделяемых блокировок чтения: сеансы чтения и сеансы записи данн1х могуг взаимно блокировать друг друга, и часто так и происходит. Как уже было показано, в Oracle обеспечивается согласованность по чтению на уровне операторов без блокирования сеансов записи сеансами чтения или взаимного блокирования. Сервер Oracle никогда не использует разделяемые блокировки чтения. Разработчики Oracle выбрали более сложную в реализации, но обеспечивающую принципиально более высокую степень параллелизма схему многовариантного доступа. Предотвращение потери изменений Чаще всего уровень изолированности транзакции REPEATABLE READ используется для предотвращения потери изменений. При установке уровня REPEATABLE READ это не происходит. По определению (повторяющиеся чтения) повторное чтение строки в той же транзакции даст точно такой же результат. В других СУБД, кроме Oracle, REPEATABLE READ может реализовываться с помощью SELECT FOR UPDATE и разделяемых блокировок чтения. Если два пользователя выбрали одну и ту же строку для изменения, они оба устанавливают на нее разделяе- мую блокировку чтения. Когда первый пользователь попытается изменить строку, эта попытка будет заблокирована. В случае попытки второго пользователя выполнить изменение возникнет взаимная блокировка. Это не идеально, но предотвращает потерю изменений. Если в Oracle необходим уровень изолированности REPEATABLE READ, но не хочется физически упорядочивать доступ к таблице с помощью операторов SELECT FOR UPDATE NOWAIT (как было продемонстрировано в начале главы), придется устанавливать уровень изолированности SERIALIZABLE. SERIALIZABLE покрывает все более либеральные уровни изолированности, поэтому, если можно выполнять доступ уровня SERIALIZABLE, то возможен доступ и уровня REPEATABLE READ. В Oracle транзакция с уровнем изолированности SERIALIZABLE реализуется так, что согласованность по чтению, обычно получаемая на уровне оператора, распространяется на всю транзакцию. То есть результаты каждого выполняемого в транзакции запроса соответствуют состоянию базы данных на момент начала транзакции. Если выполнить в этом режиме: Select * from T; Begin dbms lock.sleep(60*60*24); end; Select * from T; результаты, возвращаемые из таблицы Т, будут одинаковыми и через 24 часа (или мы получим сообщение об ошибке ORA-1555, snapshot too old). Уровень изолированности гарантирует, что эти два запроса всегда будут возвращать одинаковые результаты. В Oracle это достигается тем же способом, что и согласованность по чтению одного запроса. Сервер использует сегменты отката для воссоздания данных в том виде, как они были на момент начала транзакции, а не на момент начала выполнения оператора. Если же мы пытаемся изменить данные в транзакции с уровнем изолированности SERIALIZABLE и обнаруживаем, что данные изменились после ее начала, мы получаем сообщение об ошибке, информирующее о том, что невозможно обеспечить последовательность доступа. Вскоре мы рассмотрим это подробнее. Понятно, что этот подход не оптимален в случае рассмотренного в начале главы приложения для отдела кадров. В этом приложении типична ситуация, когда оба пользователя запрашивают данные, а затем оба изменяют их на экране. Первый пользователь пытается сохранить изменения, и у него это успешно получается. Второй же пользователь при попытке сохранить изменения получит сообщение об ошибке. Он зря потратил время. Ему придется перезапустить транзакцию, получить произошедшие за это время изменения и сделать все сначала. Потеря изменений предотвращается, но ценой дополнительных неудобств для пользователя. Однако если ситуация требует использования уровня изолированности транзакции REPEATABLE READ и не предполагается одновременное изменение несколькими транзакциями одних и тех же строк, то использование уровня изолированности SERIALIZABLE вполне допустимо. Уровень изолированности SERIALIZABLE Этот уровень изолированности транзакции обычно считают наиболее ограничивающим, но он обеспечивает самую высокую степень изолированности. Транзакция с уровнем изолированности SERIALIZABLE работает в среде, где как бы нет других пользо-
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |