Управление одновременным доступом, согласованность

dbstalker, 03 июня

Проблема одновременного доступа возникает только в многопользовательской среде. В чем состоит эта проблема?

Как в ORACLE обеспечивается одновременный доступ и достоверность данных? Что такое многоверсионность, где же храняться версии данных,согласованность ( согласованность на уровне запроса, согласованность на уровне транзакции), уровни изолированности транзакций? Основные вопросы, поднимаемые в этой статье и попытка на них ответить.

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

  • соперничество - необходимо координировать одновременный доступ к данным со стороны пользователей.
  • согласованность - пользователь всегда видит согласованный образ всех данных.

В СУБД ORACLE одновременный доступ и достоверность информации обеспечивается использованием блокировок, транзакций и многоверсионной модели согласованности данных. О блокировках и транзакциях можно почитать в других статьях. Здесь же будет обращено внимание в основном на понятие многоверсионности.

Многоверсионность в контексте СУРБД ORACLE означает одновременную поддержку множества версий данных. Как в ORACLE реализована многоверсионность? Для этого рассмотрим некоторые механизмы.

СУРБД ORACLE обеспечивает согласованность по чтению на двух различных уровнях: на уровне команды и на уровне транзакции.

Согласованность по чтению на уровне запроса.

ORACLE всегда обеспечивает согласованность по чтению на уровне запроса и при этом данные НИКОГДА не блокируются для чтения другими процессами (это фундаментальное отличие oracle от других баз данных). Гарантируется, что данные, возвращаемые запросом, согласованы по отношению к моменту начала этого запроса. Запрос никогда не видит никаких изменений, произведенных транзакциями, которые подтверждены в ходе выполнения данного запроса. Как это обеспечивается? ORACLE запоминает текущий SCN (системный номер изменения) как только запрос начинает выполняться. Далее запросу доступны только те данные, которые были зафиксированы к моменту запомненного SCN. Операторы SELECT, INSERT с запросом, UPDATE и DELETE, всегда опрашивают данные (явный или неявный запрос на выборку) и все они получают для обработки согласованное множество данных. Замечание: запросы, используемые операторами INSERT, UPDATE и DELETE, получают согласованное множество данных, но они не видят изменений, осуществляемых самими этими операторами.

Согласованность по чтению на уровне транзакции.

В ORACLE предусмотрена необязательная возможность согласованности по чтению на уровне транзакции, которая гарантирует, что данные, которые видят ВСЕ запросы внутри одной и той же транзакции, согласованы по отношению к одной точке времени (моменту начала транзакции). Методы, которыми ORACLE достигает этого:

  • транзакции read-only . Транзакции read-only (только-чтение) содержат только запросы на выборку и не содержат никаких предложений DML. ORACLE запоминает момент начала транзакции. В течение транзакции ей доступны только подтвержденные к началу транзакции данные.
  • монопольные блокировки таблиц и строк. Если в транзакции присутствуют операторы DML, то необходимо явно запросить разделяемые блокировки по таблицам или монопольные блокировки по тем строкам, которые будут считываться неоднократно.
  • Установка уровня изолированности транзакций в serializable (об этом ниже).

Таким образом, ORACLE на конкретный момент времени для команды или транзакции предоставляет согласованную версию данных.

Где и как хранятся версии данных?

Для этого используются сегменты отката и особый механизм выполнения транзакций. Почитаем Тома Кайта:

При любом изменении данных Oracle создает записи в двух разных местах. Одна запись попадает в журналы повторного выполнения, где Oracle хранит информацию, достаточную для повторного выполнения, или "наката", транзакции. Для оператора вставки это будет вставляемая строка. Для оператора удаления это будет запрос на удаление строки в слоте X блока Y файла Z. И так далее. Другая запись — это запись отмены, помещаемая в сегмент отката. Если транзакция завершается неудачно и должна быть отменена, СУБД Oracle будет читать "предварительный" образ из сегмента отката, восстанавливая необходимые данные. Помимо отмены транзакций, СУБД Oracle использует сегменты отката для отмены изменений в блоках при их чтении, то есть для восстановления данных блока на момент начала выполнения запроса. Это позволяет читать данные, несмотря на блокировку и получать корректные, согласованные результаты, не блокируя данные.

Вывод: в базе данных имеется несколько версий одной и той же информации, по состоянию на различные моменты времени. СУБД Oracle использует эти сделанные в разное время "моментальные снимки" данных для поддержки согласованности по чтению и неблокируемости запросов. Запрос на чтение в Oracle никогда не блокируется, он никогда не станет причиной взаимной блокировки с другим сеансом и никогда не даст результат, не существующий в базе данных.

Чтобы обеспечить такую красоту ORACLE должен обеспечить следующие непростые процессы: хранение версий объектов данных; очистка устаревших версий объектов данных; затраты по актуализации запрашиваемых данных на чтение; принудительный откат транзакций в случае неактуальности считанных данных.

Теперь самое время поговорить о таком понятии как уровень изолированности транзакций, определенном стандартом SQL92.

В ANSI SQL-92 вводятся четыре уровня изолированности транзакций.

Эти четыре уровня изолированности определяются своим отношением к таким явлениям:

  • неповторяемые чтения (фантомы). В процессе одной и той же транзакции, два идентичных запроса возвращают различные данные, так как изменения, произведенные другими подтвержденными транзакциями, становятся доступными. То есть критериям запроса стало удовлетворять больше данных, чем раньше. (Р3)
  • невоспроизводимость при чтении. Если строка читается в момент времени Т1 , затем перечитывается в момент времени Т2, то за это время строка могла измениться: могла быть удалена или обновлена. (Р2)
  • грязное чтение. Суть состоит в том, что допускается чтение незафиксированных (грязных) данных. (Р1)
  • Сериализуемость определяется как режим выполнения операций конкурирующих транзакций, дающий в точности тот же результат, что и последовательное выполнение этих транзакций в некотором порядке.

Следующая таблица однозначно разграничивает уровни изолированности транзакций.

Сервер ORACLE явно поддерживает уровни изолированности READ COMMITED(достигается согласованность по чтению) и SERIALIZABLE.

Рассмотрим каждый уровень изолированности и покажем, какие все СУБД плохие, а только ORACLE хороший (розовый и пушистый).

READ UNCOMMITED

Уровень изолированности READ UNCOMMITED разрешает грязное чтение. Сервер Oracle не использует грязного чтения, и не допускает его. Для чего нужен этот уровень? Для того, чтобы обеспечит неблокирующее чтение.

Но мы знаем, что фундаментальным свойством ORACLE есть согласованное неблокирующее чтение. То есть в ORACLE происходит так: когда запрос на чтение натыкается на заблокированную запись, которая изменяется другой транзакцией, oracle восстанавливает необходимую запись на момент начала выполнения нашего запроса и выдает результат не ожидая, когда транзакция, которая изменяет данные, будет зафиксирована. То есть блокировка просто будет обойдена.

Другие же СУБД, чтобы транзакции на чтение не зависали от блокировок, вынуждены допускать чтение незафиксированных данных. То есть большая вероятность получения недостоверных данных.

READ COMMITED

Этот уровень изолированности требует, чтобы транзакция читала только данные, зафиксированные до её начала. Не допускается грязное чтение, но разрешены чтения фантомов и невоспроизводимость чтения. Многоверсионность и согласованность запросов по чтению позволяют ораклу получить один и тот же результат как при уровне изолированности READ UNCOMMITTED так и при READ COMMITED. Просто ORACLE, в случае необходимости, из сегментов отката восстановит на соответствующий момент времени изменённые данные. В других же СУБД необходимо ждать снятия блокировки и к тому же могут быть прочитаны данные, изменённые и зафиксированные другими транзакциями в момент времени после начала чтения.

REPEATABLE READ

Этот уровень изолированности требует согласованности, корректности данных без потери изменений.В ORACLE мы получаем данные, согласованные на момент начала запроса. В других СУБД обычно получают согласованный результат на момент завершения запроса. В этих системах сеансы на чтение блокируют сеансы на запись и сеансы на запись блокируют сеансы на чтение. А все из-за использования разделяемых блокировок на чтение. ORACLE же никогда не использует разделяемые блокировки на чтение. Предотвратить потерю изменений - это значит, добиться того, чтобы повторное чтение строки в продолжение транзакции давало один и тот же результат.

В других СУБД этого добиваются путем разделяемых блокировок на чтение и select for update. Но это чревато взаимными блокировками. В ORACLE это можно добиться путем упорядочивания доступа к таблице с помощью select for update nowat или установить уровень изолированности serializable.

SERIALIZABLE

Это самая высокая степень изолированности, имитация однопользовательской среды. Для транзакции база данных выглядит как моментальный снимок. Все изменения проведенные другими транзакциями не видны. В Oracle это реализовано путем распространения согласованности по чтению на уровне оператора на уровень транзакции. То есть из сегментов отката ORACLE восстанавливает данные не на начало оператора, а на начало транзакции. Но есть один неприятный момент: при попытке обновления строки измененной после начала транзакции будет получена ошибка ORA-08177. Это все потому, что ORACLE при этом уровне изолированности придерживается оптимистического подхода: предполагается, что данные, которые будет изменять наша транзакция, никто не изменит в процессе нашей транзакции.

Уровень изолированности serializable повысит вероятность неудачного завершения транзакции при увеличении ее продолжительности. Он подходит как раз для сред с большой частотой транзакций, когда они короткие и выполняются быстро, читая и изменяя данные. Такой уровень изолированности не подходит для многих систем.

Транзакции только для чтения

Достаточно часто необходимо пользователю сформировать какой-то отчет по данным согласованным по чтению на определенный момент. Другие СУБД для этого используют уровень REPEATABLE READ и получая дополнительно нежелательные последствия разделяемого блокирования по чтению. В ORACLE для таких ситуаций используются транзакции только для чтения. При таком виде транзакции не возникнет ошибки ORA-08177, но будет в некоторых случаях (слишком долго формируете отчет) ошибка ORA-1555.

8 комментариев

Прокоментировать

Vladimir
20 мая 2008 г. в 13:43

"ORACLE запоминает текущий SCN (системный номер изменения) как только запрос начинает выполняться." Разве SCN генерится не только в случае изменения данных, а ещё и при обычном селекте?

dbstalker
20 мая 2008 г. в 18:23

Запрос на выборку НЕ провоцирует генерацию SCN , а вызывает ЗАПОМИНАНИЕ SCN, который сгенерирован какой-то транзакцией.

Vladimir
21 мая 2008 г. в 11:23

Я, конечно, извиняюсь за назойливость, но непонятные моменты хочется прояснять. Что имелось ввиду под фразой "какой-то транзакцией"? Допустим, из внешней среды вызывается функция, которая только выполняет селект и возвращает данные. По какому SCN-у она будет выбирать данные, откуда она его возьмёт?

dbstalker
22 мая 2008 г. в 11:57

Ваше внимание очень приятно. На Ваш вопрос отвечу так как я понимаю (на самом деле все несколько иначе, но так проще для понимания. Хотя суть такая же): начинается транзакция. Оракл фиксирует текущий SCN (в любой момент времени можно выполнить запрос select dbms_flashback.get_system_change_number from dual и Вы тоже сможете узнать на данное время какой текущий SCN в базе). Этот номер дальше назовем начальным для нашей транзакции. Этот номер для каждой активной транзакции можно увидеть в поле START_SCNB представления v$TRANSACTION. Дальше данные обрабатываются после предварительной сверки начального SCN c SCN выбраных данных исходя из многоверсионности данных в оракле.Во время фиксации транзакции генерируется SCN данной транзакции. Текущий SCN также можно узнать по запросу select max(ktuxescnw * power(2, 32) + ktuxescnb) from x$ktuxe. Но вы должны знать, что в базе существуют тьма разных SCN: current SCN, commit SCN, transaction start SCN, checkpoint SCN...

Vladimir
22 мая 2008 г. в 18:07

Спасибо! Вроде бы ясно. То есть, перед выполнением запроса берётся последний сформированный SCN (то есть, по последней завершённой транзакции), относительно которого выбираются данные. Интересно было бы узнать про разновидности SCN-ов..

dbstalker
23 мая 2008 г. в 09:38

По поводу commit SCN и transaction start SCN- нужно почитать про транзакции, о checkpoint SCN - про контрольные точки. Об этом есть немного материала на моем блоге. О current SCN - Вы уже знаете из моего предыдущего ответа. Больше информации по SCN будет в материале об undo, который я все ещё планируюю выложить на свой блог.

den
17 июня 2008 г. в 00:39

# неповторяемые чтения (фантомы). В процессе одной и той же транзакции, два идентичных запроса возвращают различные данные, так как изменения, произведенные другими подтвержденными транзакциями, становятся доступными. То есть критериям запроса стало удовлетворять больше данных, чем раньше. (Р3)
# невоспроизводимость при чтении. Если строка читается в момент времени Т1 , затем перечитывается в момент времени Т2, то за это время строка могла измениться: могла быть удалена или обновлена. (Р2)

можно более подробно описать разницу между этими двумя пунктами? Написано так, буд-то это одно и то же, только правило Р3 действует на всю транзакцию, а Р2 - только на оператор...

Vladimir
17 июня 2008 г. в 10:16

Извиняюсь, попробую ответить, так как сам в своё время вникал в разницу. Если прочитать внимательно, то, собственно, у автора всё, что нужно, написано.
А конкретно - дело в том, что речь идёт о данных по отношению к запросу.
В первом случае при чтении фантомов (Р3), условиям запроса удовлетворяет больше данных. Во втором же случае (Р2) речь идёт об изменённых данных, которые были выбраны в первом запросе, но не попали вследствие их изменения или удаления в выборку второго запроса.
На первый взгляд кажется, что разницы никакой, но всё-таки имеется принципиальная разница в теоретическом плане, при попытках всеобъять, так сказать. Поскольку в разных СУБД различна реализация согласованности данных и транзакционного процесса, это влечёт за собой особенности работы с данными в каждой конкретной СУБД, и возможно, что в пользу масштабируемости, им приходится жертвовать получением достоверных данных вместе с получением описанных "побочных" явлений. А иначе зачем, кроме как не по причине ограниченности ресурсов, всё это было придумано?
Надо сказать, что оракловый транзакционный процесс не позволяет (не допускает - как угодно) полного соответствия явлений при выборке данных представленной "модели" явлений ANSI.

 

Новый комментарий

Я не спамер: введите суму 1+8



 

От авторов блога

О Блоге - прочитай перед началом.

Задать вопрос и получить ответ - уже решено 94 вопросов

Глоссарий - список терминов и сокращений


 
 

Бизнес форум

Последние темы:

Требуется бухгалтер
21 августа, 1 ответа