Параллельная работа с БД
Предположим, что Лига Женщин-Избирателей (ЛЖИ) из Москвы решила устроить обед с окороком и индейкой. Коммерческая Палата (КП) из Томска решила, что пришло время порадовать своих членов обедом с окороками и овощами. Каждая организация обратилась в своем городе в представительство фирмы «Хелми». ЛЖИ требует 25 окороков, КП нужно 35 окороков. Оба заказа передаются в систему БД регионального склада одновременно (рис. 5.2). Заказ ЛЖИ приходит на долю секунды раньше, чем заказ КП.
КЛ
">
ЛЖИ
"> ЛЖИ
|
Москва
КП
Томск
ЛЖИ КП
|
|
Порядок обработки ЦП
|
Рис. 5.2 Пример параллельной обработки
Образ инвентарной записи об окороках помещается в рабочую область компьютера. Запись показывает, что на складе есть 100 окороков. Но прежде чем транзакция ЛЖИ завершается и инвентарная запись обновляется, транзакция КП также приводит к созданию в рабочей области еще одной копии той же инвентарной записи, показывающей, что на складе 100 окороков. Обе записи показывают, что заказ можно выполнить.
|
Предположим, что транзакция ЛЖИ завершена первой. Переписанная инвентарная запись показывает 100-25=75 окороков, оставшихся на складе. После завершения транзакции КП инвентарная запись снова будет переписана, показывая 100-35=65 окороков на складе. В результате на самом деле получается, что из 100 окороков на складе продано 60, то есть осталось 40, но запись в системе показывает, что их осталось 65. Этот пример отражает основную сущность параллельной обработки.
ЛЖИ КП
|
|
Бесконечное Бесконечное
ожидание ожидание
Рис. 5.4 Схема блокировки, приводящая
к тупиковой ситуации
Общим способом предупреждения проблем, связанных с параллельной обработкой, является простая блокировка. В нашем примере первая транзакция должна заблокировать инвентарную запись об окороках, т.е. запретить доступ к ней других транзакций (рис.5.3) до тех пор, пока обработка первой транзакции не будет завершена. Когда запись заблокирована одним пользователем, никакой другой пользователь не может обращаться к ней для обновления.
Предположим, что и ЛЖИ, и КП хотят заказать окорока и индеек (рис.5.4). ЛЖИ сначала обращается к инвентарной записи об индейках. Поскольку эта запись заблокирована, то КП сначала обращается к инвентарной записи об окороках. Таким образом, обе записи заблокированы для доступа других пользователей. Затем и ЛЖИ, и КП заканчивают обработку записей об индейках и окороках и готовы обращаться к другой записи. Однако ни один из пользователей не завершил выполнение транзакции, поэтому обе записи остаются заблокированными.
Существует несколько способов справляться с взаимоблокировкой. Один подход – фиксировать порядок обращения к записям. Т.е., если требуется обращаться к записям А и В, то к ним всегда нужно обращаться именно в таком порядке. В нашем примере при требовании записей об окороках и индейках можно заставить систему всегда сначала обращаться к записи об окороках, а затем - к записи об индейках. Когда ЛЖИ закончит обработку, все записи, к которым обращались, будут разблокированы и готовы к работе КП. Однако в результате скорость работы может упасть, поскольку ожидание увеличивает время выполнения транзакций. Более того, если транзакция слишком долго ожидает разблокирования записи, она может быть отменена и ее придется запускать заново. Обычно все это приводит к недовольству пользователей информационной системой.
Некоторые СУБД выполняют детекцию взаимоблокировки, регулярно проверяя, не слишком ли долго продолжается ожидание записи или ресурса. Другой метод детекции состоит в том, что проводится стрелка от транзакции к искомой записи, а затем стрелка от записи к транзакции, которая в данный момент ее использует. Если граф имеет петли, то это означает, что обнаружена взаимоблокировка. Это показано на рис. 5.5 .
БД
Используется Используется
Рис.5.5 Взаимоблокировка, обнаруженная при помощи
детектирующих циклов
Процедуры детекции взаимоблокировки завершают свою работу, отменяя одну из транзакций и продвигая следующую транзакцию в очереди.
Другой способ контроля параллельной обработки - двухфазная блокировка. Говорят, что транзакция следует протоколу двухфазной блокировки, если все операции блокировки - read_lock, write_lock - предшествуют первой операции разблокирования в транзакции. Опция read_lock позволяет считывать запись, а write_lock позволяет и считывать, и обновлять данную запись. Проверено, что транзакции могут выполняться так, что их результаты будут такими же, как если бы они выполнялись одна за другой без прерывания. Однако у двухфазной блокировки тоже есть свои недостатки. Она может привести к взаимоблокировке, если совместно с ней не применяется протокол предотвращения взаимоблокировки.
В качестве примера на рис. 5.6 видим процедуру двухфазной блокировки, приводящую к взаимоблокировке.
Рис.5.6 Пример двухфазной блокировки, приведшей
к взаимоблокировке
Транзакция 1 (Т1) задает опцию read_lock для записи О (окорок); одновременно Т2 задает опцию read_lock для записи И (индейка). Через два шага и Т1, и Т2 требуют опцию write_lock для той записи, которая удерживается опцией read_lock другой транзакции. Такое действие запрещено, так как в этом случае значение может быть изменено операцией записи, выполненной другой транзакцией. Тогда конкурирующий источник опции read_lock может считать неверное значение.
Применение двухфазной блокировки с протоколом предотвращения взаимоблокировки показано на рис.5.7. Добавили требование того, чтобы все элементы данных, необходимые в транзакции, блокировались заранее. Если какие-либо из элементов данных недоступны, то ни один элемент не блокируется и транзакция задерживается до тех пор, пока все нужные ей элементы не будут доступны.
Рис. 5.7. Пример двухфазной блокировки с протоколом
предотвращения взаимоблокировки
На рис.5.8 приведен пример, демонстрирующий параллельные операции, которые можно привести к последовательной форме.
Рис.5.8. Пример двухфазной блокировки с протоколом
избежания взаимоблокировки, в результате которого
выполняется последовательная обработка