В начало

Лекция 26

Параллельные транзакции. Типы конфликтов. Захваты и блокировки

 

26.1. Параллельное выполнение транзакций

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

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

Чтобы понять, как должны выполняться параллельные транзакции, рассмотрим проблемы, возникающие при параллельной работе с данными.

 

Пропавшие обновления

Рассмотрим пример работы двух диспетчеров с модифицированной БД «Сессия» (слайд 3). Пусть Диспетчер 1 вносит в текущий учебный план для каждой дисциплины, читаемой на третьем курсе, сведения о преподавателях, параллельно изменяя при этом значение столбца Нагрузка в таблице «Кадровый_состав», а Диспетчер 2 выполняет такую же операцию для дисциплин второго курса.

Диспетчер 1 начинает работу по изменению таблицы «Учебный_план» для Дисциплины 1 с количеством часов, равным 50. В столбец ID_Преподаватель для этой дисциплины предполагается внести значение 5. Запрос текущей нагрузки преподавателя возвращает значение 350, и Диспетчер 1 подтверждает изменение таблицы «Учебный_план». При этом выполняются дополнительные действия по изменению столбца Нагрузка в таблице «Кадровый_состав» для строки с ID_Преподаватель = 5 (в столбец заносится значение 400).

До завершения операции Диспетчер 2 начинает те же действия для Дисциплины 2 с количеством часов 32, которую должен читать тот же преподаватель (ID_Преподаватель = 5). Запрос текущей нагрузки преподавателя также возвращает значение 350, с которым и работает дальше Диспетчер 2. Выполнив те же операции, что и Диспетчер 1 (но после него), Диспетчер 2 помещает в столбец Нагрузка значение 382, отменив тем самым предыдущие изменения.

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

 

Чтение «грязных» данных

Другой пример коллизий при несогласованной работе двух параллельных транзакций представлен на слайде 4.

Как показано на рисунке, Диспетчер 1 и Диспетчер 2 опять выполняют действия, описанные в предыдущем примере, но Диспетчер 2 начинает запрашивать данные о нагрузке преподавателя в тот момент, когда изменения, сделанные Диспетчером 1, уже зафиксированы в столбце Нагрузка, а транзакция еще не закончилась. Запрос Диспетчера 2 возвращает значение 400, и Диспетчер 2 вынужден отменить свои действия потому, что 400 часов – это максимальное разрешенное значение нагрузки. Между тем транзакция Диспетчера 1 закончилась возвратом к исходному состоянию, т.е. на самом деле Диспетчер 2 мог бы успешно завершить операцию.

Это тоже не соответствует требованию изолированности пользователей (каждый пользователь начинает свою транзакцию при согласованном состоянии базы данных и в праве ожидать увидеть согласованные данные). Чтобы избежать ситуации чтения «грязных» данных, необходимо требовать, чтобы до завершения одной транзакции, изменившей некоторый объект, никакая другая транзакция не могла читать изменяемый объект.

 

Чтение несогласованных данных

Рассмотрим ситуацию, которая приводит к получению несогласованных данных при выполнении операций над БД.

По-прежнему Диспетчер 1 выполняет операцию по заполнению строки учебного плана, а Диспетчер 2 при выполнении своей операции должен сделать выбор между двумя преподавателями в соответствии с их текущей нагрузкой.

Начиная работу практически одновременно с Диспетчером 1, Диспетчер 2 получает следующие сведения: нагрузка первого преподавателя (ID_Преподаватель = 5) составляет 350 часов, а нагрузка второго преподавателя (ID_Преподаватель = 7) составляет 370 часов. Далее Диспетчер 2 принимает решение в пользу первого преподавателя, но повторный запрос нагрузки возвращает значение 400, т.к. Диспетчер 1 уже сохранил новые данные в таблице «Кадровый_состав».

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

 

Строки-призраки

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

Диспетчер 1 инициирует Транзакцию 1, которая выполняет оператор выборки строк таблицы в соответствии с некоторым условием (например, формирование списка студентов, сдавших дисциплину с ID_Дисциплина = N, по таблице «Сводная_ведомость»). До завершения Транзакции 1 Транзакция 2, вызванная Диспетчером 2, вставляет в таблицу «Сводная_ведомость» новую строку, удовлетворяющую условию отбора Транзакции 1 (данные о результате сдачи дисциплины N еще одним студентом), и успешно завершается. Транзакция 1 повторно выполняет оператор выборки, и в результате появляется строка, которая отсутствовала при первом выполнении оператора. Конечно, такая ситуация противоречит идее изолированности транзакций.

 

26.2. Сериализация транзакций (слайд 7)

Чтобы добиться изолированности транзакций, СУБД должна использовать специальные методы регулирования совместного выполнения транзакций.

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

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

 

На самом деле между транзакциями могут существовать следующие виды конфликтов (слайд 8):

1)      Транзакция 2 пытается изменять объект, измененный не закончившейся Транзакцией 1 (W-W – конфликт);

2)      Транзакция 2 пытается изменять объект, прочитанный не закончившейся Транзакцией 1 (R-W – конфликт);

3)      Транзакция 2 пытается читать объект, измененный не закончившейся Транзакцией 1 (W-R – конфликт).

Практические методы сериализации транзакций основываются на учете этих конфликтов.

 

26.3. Захват и освобождение объекта

Для обеспечения сериализации транзакций применяются методы «захвата» и «освобождения» объектов, производимого по инициативе транзакции: транзакция «захватывает» объект, что приводит к его блокировке для других транзакций, и освобождает его только при своем завершении. При этом захваты объектов несколькими транзакциями на чтение совместимы (т.е. нескольким транзакциям разрешается читать один и тот же объект), захват объекта одной транзакцией на чтение не совместим с захватом другой транзакцией того же объекта на запись, и захваты одного объекта разными транзакциями на запись не совместимы. Тем самым, выделяются два основных режима захватов (слайд 9):

4)      совместный режим - S (Shared), означающий разделяемый захват объекта и необходимый для выполнения операции чтения объекта;

5)      монопольный режим - X (eXclusive), означающий монопольный захват объекта и необходимый для выполнения операций записи, удаления и модификации.

 

Наиболее распространенным в СУБД, основанных на архитектуре «клиент-сервер», является подход, реализующий соблюдение двухфазного протокола захватов объектов БД (слайд 10). В общих чертах протокол состоит в том, что перед выполнением любой операции над объектом базы данных от имени транзакции запрашивается захват объекта в соответствующем режиме (в зависимости от вида операции – совместном или монопольном). В соответствии с этим протоколом выполнение транзакции разбивается на две фазы: первая фаза транзакции - накопление захватов; вторая фаза (фиксация или откат) - освобождение захватов.

При соблюдении двухфазного протокола основная проблема состоит в том, что следует считать объектом для захвата?

 

В контексте реляционных баз данных возможны следующие варианты (слайд 11):

6)      файл - физический (с точки зрения базы данных) объект, область хранения нескольких отношений и, возможно, индексов;

7)      таблица - логический объект, соответствующий множеству записей данного отношения;

8)      страница данных - физический объект, хранящий записи одного или нескольких отношений, индексную или служебную информацию;

9)      запись - элементарный физический объект базы данных.

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