Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
УМК по СРОД-10-12.doc
Скачиваний:
10
Добавлен:
13.11.2018
Размер:
2.55 Mб
Скачать

3.4. Элементы блокировки.

    База данных может быть разбита на элементы, то есть на части, которые можно блокировать. Блокируя некоторый элемент, транзакция может препятствовать доступу других транзакций к этому элементу до тех пор, пока они не разблокируют его.        Природа и размер элементов являются спорными вопросами. Можно видеть большие элементы, подобные отношениям, или малые, такие как отдельные кортежи и даже компоненты кортежей. Выбор больших элементов сокращает накладные расходы системы по поддержанию блокировок, тогда как выбор малых элементов даёт возможность параллельного исполнения многих транзакций. Если типичная транзакция читает или модифицирует один кортеж, который она находит с помощью индекса, то целесообразно трактовать кортежи как элементы. Если же типичная транзакция производит соединение двух или более отношений и тем самым требует доступа ко всем кортежам этих отношений, уместно в качестве элементов выбрать отношения.     Рассмотрим теперь программу, которая при взаимодействии с базой данных не только читает, но и записывает её элементы, но и блокирует и разблокирует их. Условимся, что элемент должен быть блокирован до начала чтения или записи и что операция блокировки действует как примитив синхронизации. Это означает, что если транзакция пытается блокировать уже блокированный элемент, ей приходится ждать, пока блокировка не будет снята по команде разблокирования, которая выполняется транзакцией, устанавливающей блокировку.     Каждая программа, в конце концов, должна разблокировать любой элемент, который она блокировала. Расписание элементарных шагов двух или более транзакций, такое, что выполняются указанные выше правила, касающиеся блокировок, называются легальным.  Пример 3.2. Программу Р из примера 3.1 можно было бы записать с блокировками следующим образом:

P: LOCK A; READ A; A:=A+1; WRITE A; UNLOCK A;

Пусть Т1 и Т2 - две исполнимых программы Р. При выполнении Т1 произойдёт блокировка. Если Т2 начинается до завершения Т1, то система заставит её ждать            A:=A+1                                   Т1 |----------------------|  

Т2  - - - - - - - - |--------------------|  Совместным результатом окажется увеличение А на 2.

Бесконечное ожидание и тупики

    Пусть существует механизм блокировки. В примере 2 блокировка предоставляется Т2 после снятия её транзакцией Т1. Рассмотрим теперь иную ситуацию. Пусть во время пребывания Т2 в состоянии ожидания транзакция Т3 только запрашивает блокировку А, и этот запрос выполняется раньше, чем запрос Т2. Далее, в то время, когда блокировку установила транзакция Т3, её запрашивает Т4, чьё требование удовлетворяется после разблокирования А транзакцией Т3 и т.д. Очевидно, при этом не исключена возможность того, что Т2 будет бесконечно находиться в состоянии ожидания.

Т1: LOCK A; UNLOCK A;

T2: – – – – – – –  -– – – – – – –  - - - - - - - - - T3: LOCK A; UNLOCK A; T4:LOCK A; UNLOCK A; T5:. .

    Тогда как некоторые другие транзакции постоянно осуществляют блокировку А, хотя и существуют неограниченное число моментов, когда транзакция Т2 имеет шансы заблокировать А.    Состояние такого рода называют  бесконечным ожиданием. Подобная проблема потенциально может возникнуть в любой обстановке, предусматривающей параллельное исполнение процессов. Простой способ избежать бесконечного ожидания заключается в том, что система предоставления блокировок должна регулировать все неудовлетворённые немедленно запросы и предоставлять возможность блокировки элемента А после его разблокирования первый запросившей её транзакции из числа ожидающих. Эта стратегия ("первым вошёл - первым обслуживается") устраняет бесконечные ожидания.

Более серьёзная проблема, которая возникает при параллельной обработке - это так называемые «тупики».

Пример 3.3. Пусть имеются две транзакции Т1 и Т2, основными действиями, которых при параллельной обработке являются следующие:

Т1: lock A; lock B; unlock A; unlock B;

T2: lock B; lock A; unlock B; unlock A;

        Здесь не имеет значения, что конкретно делают с элементами А и В эти транзакции. Пусть они начинают использоваться примерно в одно и то же время. Транзакция Т1 запрашивает блокировку А и её запрос удовлетворяется. Точно так же удовлетворяется запрос транзакции Т2 на блокировку В затем Т1 запрашивает блокировку В и вынуждает ждать, поскольку этот элемент заблокирован транзакцией Т2. Аналогично Т2 запрашивает блокировку А и должна ждать, пока Т1 разблокирует А. Таким образом ни одна транзакция не может продолжаться. Каждая из них ожидает пока другая разблокирует требуемый для неё элемент. Поэтому Т1 и Т2 будут ждать бесконечно.     Ситуация, при которой каждая из множеств S двух или более транзакций ожидает, когда ей будет предоставлена возможность заблокировать элемент, заблокированный в данный момент времени какой-либо иной транзакцией их данного множества S, называют тупиком.        Так как все транзакции находятся в состоянии ожидания, то одна из них не может разблокировать элемент, необходимый для продолжения другой транзакции из S. Поэтому ожидание для них становится бесконечным.

Способы предотвращения тупиков

1).    Потребовать, чтобы каждая транзакция единовременно запрашивала все нужные ей блокировки. В нашем примере система предоставила бы блокировку как А, так и В для транзакции Т1, если бы она запросила блокировку первой. После завершения Т1 обе эти блокировки могли бы быть установлены для Т2, то есть Т2 должна ждать. 2).    Ввести произвольное линейное упорядочивание элементов и потребовать, чтобы все транзакции запрашивали блокировки в этом порядке.

Пусть в нашем примере А предшествует В, тогда затребовав блокировку А перед В, Т2 обнаруживает, что А уже заблокирована Т1. Элемент В не был ещё заблокирован для Т2. поэтому для Т1 доступна блокировка В, когда она будет запрашиваться. При завершении Т1 блокировки на А и В снимаются и Т2 может продолжаться. 3).    Ничего не предпринимать для их предотвращения, а периодически проверять запросы на блокировки и выявлять, не возникло ли тупиковой ситуации. Облегчает предотвращение такой проверки алгоритм построения графа, вершины которого представляют транзакции, а дуга T1->Т2 означает, что транзакция Т2 ожидает выполнения её запроса на блокировку элемента, заблокированного в данный момент транзакцией Т2. каждый цикл указывает тупик. Если же циклов нет, не существует и тупиков. В случае обнаружения тупика следует произвести рестарт хотя бы для одной из попавших в него транзакций и её воздействие на БД должно быть аннулировано.