2. Транзакции и параллелизм.

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

Опр2. Набор из нескольких транзакций, элементарные операции которого чередуются друг с другом, называется смесью транзакций.

Опр3. Последовательность, в которой выполняются  операции данной смеси транзакций, называется графиком выполнения.

Задача СУБД, таким образом, сводиться к нахождению такой последовательности операций, чтобы

  1. Была обеспечена изолированность пользователей (транзакций).
  2. Давать минимальное возможное значение времени выполнения транзакции.

Каким образом транзакции различных пользователей могут мешать друг другу?

1. Потеря результатов обновления.

Две транзакции записывают некоторые данные в одни и те же строки и фиксируют результат.

2. Проблема незафиксированной зависимости.

Транзакция B изменяет данные в строке. После этого транзакция A читает измененные данные и работает с ними. Транзакция B откатывается и восстанавливает старые данные.

Получается, что транзакция A работала с данными, которых и в помине нет в базе данных.

3. Проблема несовместимого анализа.

А. Неповторимое считывание.

Б. Фантомное чтение.

В. Собственно несовместимый анализ.

Неповторяемое чтение. Транзакция A дважды читает одну и ту же строку. Между этими чтениями вклинивается транзакция B, которая изменяет значения в строке.

Транзакция A ничего не знает о существовании транзакции B, и, т.к. сама она не меняет значение в строке, то ожидает, что после повторного чтения значение будет тем же самым.

Фантомное чтение. Эффект фиктивных элементов несколько отличается от предыдущих транзакций тем, что здесь за один шаг выполняется достаточно много операций - чтение одновременно нескольких строк, удовлетворяющих некоторому условию.

Транзакция A дважды выполняет выборку строк с одним и тем же условием. Между выборками вклинивается транзакция B, которая добавляет новую строку, удовлетворяющую условию отбора.

Транзакция A ничего не знает о существовании транзакции B, и, т.к. сама она не меняет ничего в базе данных, то ожидает, что после повторного отбора будут отобраны те же самые строки.

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

Длинная транзакция выполняет некоторый анализ по всей таблице, например, подсчитывает общую сумму денег на счетах клиентов банка для главного бухгалтера. Пусть на всех счетах находятся одинаковые суммы, например, по $100. Короткая транзакция в этот момент выполняет перевод $50 с одного счета на другой так, что общая сумма по всем счетам не меняется.

Результат. Хотя транзакция B все сделала правильно - деньги переведены без потери, но в результате транзакция A подсчитала неверную общую сумму.

Конфликты между транзакциями

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

Однако не всякие транзакции мешают друг другу. Очевидно, что транзакции не мешают друг другу, если они обращаются к разным данным или выполняются в разное время.

Опр. 4. Транзакции называются конкурирующими, если они пересекаются по времени и обращаются к одним и тем же данным.

В результате конкуренции за данными между транзакциями возникают конфликты доступа к данным. Различают следующие виды конфликтов:

Конфликты типа R-R (Чтение - Чтение) отсутствуют, т.к. данные при чтении не изменяются.

Опр. 5. График запуска набора транзакций называется последовательным, если транзакции выполняются строго по очереди, т.е. элементарные операции транзакций не чередуются друг с другом.

Опр. 6. Если график запуска набора транзакций содержит чередующиеся элементарные операции транзакций, то такой график называется чередующимся.

При выполнении последовательного графика гарантируется, что транзакции выполняются правильно, т.е. при последовательном графике транзакции не "чувствуют" присутствия друг друга.

Опр. 7. Два графика называются эквивалентными, если при их выполнении будет получен один и тот же результат.

Опр. 8. График запуска транзакции называется верным (сериализуемым), если он эквивалентен какому-либо последовательному графику.

3. Блокировки.

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

Различают два типа блокировок:

Если транзакция A блокирует объект при помощи X-блокировки, то всякий доступ к этому объекту со стороны других транзакций отвергается.

Если транзакция A блокирует объект при помощи S-блокировки, то

Правила взаимного доступа к заблокированным объектам можно представить в виде следующей матрицы совместимости блокировок. Если транзакция A наложила блокировку на некоторый объект, а транзакция B после этого пытается наложить блокировку на этот же объект, то успешность блокирования транзакцией B объекта описывается таблицей:

 

Транзакция B пытается наложить блокировку:

Транзакция A наложила блокировку:

S-блокировку

X-блокировку

S-блокировку

Да

НЕТ
(Конфликт
R-W)

X-блокировку

НЕТ
(Конфликт
W-R)

НЕТ
(Конфликт
W-W)

Три случая, когда транзакция B не может блокировать объект, соответствуют трем видам конфликтов между транзакциями.

Доступ к объектам базы данных на чтение и запись должен осуществляться в соответствии со следующим протоколом доступа к данным:

  1. Прежде чем прочитать объект, транзакция должна наложить на этот объект S-блокировку.
  2. Прежде чем обновить объект, транзакция должна наложить на этот объект X-блокировку. Если транзакция уже заблокировала объект S-блокировкой (для чтения), то перед обновлением объекта S-блокировка должна быть заменена X-блокировкой.
  3. Если блокировка объекта транзакцией B отвергается оттого, что объект уже заблокирован транзакцией A, то транзакция B переходит в состояние ожидания. Транзакция B будет находиться в состоянии ожидания до тех пор, пока транзакция A не снимет блокировку объекта.
  4. X-блокировки, наложенные транзакцией A, сохраняются до конца транзакции A.

Проблема потери результатов обновления

Две транзакции по очереди записывают некоторые данные в одну и ту же строку и фиксируют изменения.

Обе транзакции успешно накладывают S-блокировки и читают объект . Транзакция A пытается наложить X-блокирокировку для обновления объекта . Блокировка отвергается, т.к. объект уже S-заблокирован транзакцией B. Транзакция A переходит в состояние ожидания до тех пор, пока транзакция B не освободит объект. Транзакция B, в свою очередь, пытается наложить X-блокирокировку для обновления объекта . Блокировка отвергается, т.к. объект уже S-заблокирован транзакцией A. Транзакция B переходит в состояние ожидания до тех пор, пока транзакция A не освободит объект.

Результат. Обе транзакции ожидают друг друга и не могут продолжаться. Возникла ситуация тупика.

Проблема незафиксированной зависимости (чтение "грязных" данных, неаккуратное считывание)

Транзакция B изменяет данные в строке. После этого транзакция A читает измененные данные и работает с ними. Транзакция B откатывается и восстанавливает старые данные.

Транзакция A

Время

Транзакция B

---

S-блокировка - успешна

---

Чтение

---

X-блокировка - успешна

---

Запись

S-блокировка - отвергается

---

Ожидание…

Откат транзакции

(Блокировка снимается)

S-блокировка - успешна

---

Чтение

---

Работа с прочитанными данными

---

---

---

Фиксация транзакции

---

Все правильно

 

 

Результат. Транзакция A притормозилась до окончания (отката) транзакции B. После этого транзакция A продолжила работу в обычном режиме и работала с правильными данными. Конфликт разрешен за счет некоторого увеличения времени работы транзакции A (потрачено время на ожидание снятия блокировки транзакцией B).

Проблема несовместимого анализа

Неповторяемое считывание

Транзакция A дважды читает одну и ту же строку. Между этими чтениями вклинивается транзакция B, которая изменяет значения в строке.

Транзакция A

Время

Транзакция B

S-блокировка - успешна

---

Чтение

---

---

X-блокировка - отвергается

---

Ожидание…

Повторное чтение

Ожидание…

Фиксация транзакции
(Блокировка снимается)

Ожидание…

---

X-блокировка - успешна

---

Запись

---

Фиксация транзакции

(Блокировка снимается)

Все правильно

 

 

Результат. Транзакция B притормозилась до окончания транзакции A. В результате транзакция A дважды читает одни и те же данные правильно. После окончания транзакции A, транзакция B продолжила работу в обычном режиме.

Фиктивные элементы (фантомы)

Транзакция A дважды выполняет выборку строк с одним и тем же условием. Между выборками вклинивается транзакция B, которая добавляет новую строку, удовлетворяющую условию отбора.

Транзакция A

Время

Транзакция B

S-блокировка строк, удовлетворяющих условию .
(Заблокировано n строк)

---

Выборка строк, удовлетворяющих условию .
(Отобрано n строк)

---

---

Вставка новой строки, удовлетворяющей условию .

---

Фиксация транзакции

S-блокировка строк, удовлетворяющих условию .
(Заблокировано n+1 строка)

---

Выборка строк, удовлетворяющих условию .
(Отобрано n+1 строк)

---

Фиксация транзакции

---

Появились строки, которых раньше не было

 

 

Результат. Блокировка на уровне строк не решила проблему появления фиктивных элементов.

Собственно несовместимый анализ

Длинная транзакция выполняет некоторый анализ по всей таблице, например, подсчитывает общую сумму денег на счетах клиентов банка для главного бухгалтера. Пусть на всех счетах находятся одинаковые суммы, например, по $100. Короткая транзакция в этот момент выполняет перевод $50 с одного счета на другой так, что общая сумма по всем счетам не меняется.

Транзакция A

Время

Транзакция B

S-блокировка счета - успешна

---

Чтение счета и суммирование.

---

---

X-блокировка счета - успешна

---

Снятие денег со счета .

---

X-блокировка счета - отвергается

---

Ожидание…

S-блокировка счета - успешна

Ожидание…

Чтение счета и суммирование.

Ожидание…

S-блокировка счета - отвергается

Ожидание…

Ожидание…

Ожидание…

Ожидание…

 

Ожидание…

Результат. Обе транзакции ожидают друг друга и не могут продолжаться. Возникла ситуация тупика.

Разрешение тупиковых ситуаций

Итак, при использовании протокола доступа к данным с использованием блокировок часть проблем разрешилось (не все), но возникла новая проблема - тупики:

Общий вид тупика (dead locks) следующий:

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

Т.к. нормального выхода из тупиковой ситуации нет, то такую ситуацию необходимо распознавать и устранять. Методом разрешения тупиковой ситуации является откат одной из транзакций (транзакции-жертвы) так, чтобы другие транзакции продолжили свою работу. После разрешения тупика, транзакцию, выбранную в качестве жертвы можно повторить заново.

Можно представить два принципиальных подхода к обнаружению тупиковой ситуации и выбору транзакции-жертвы:

  1. СУБД не следит за возникновением тупиков. Транзакции сами принимают решение, быть ли им жертвой.
  2. За возникновением тупиковой ситуации следит сама СУБД, она же принимает решение, какой транзакцией пожертвовать.

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

Второй способ характерен для промышленных СУБД (ORACLE, MS SQL Server и т.п.). В этом случае система сама следит за возникновением ситуации тупика, путем построения (или постоянного поддержания) графа ожидания транзакций. Граф ожидания транзакций - это ориентированный двудольный граф, в котором существует два типа вершин - вершины, соответствующие транзакциям, и вершины, соответствующие объектам захвата. Ситуация тупика возникает, если в графе ожидания транзакций имеется хотя бы один цикл. Одну из транзакций, попавших в цикл, необходимо откатить, причем, система сама может выбрать эту транзакцию в соответствии с некоторыми стоимостными соображениями (например, самую короткую, или с минимальным приоритетом и т.п.).

Hosted by uCoz