Защита данных – параллелизм
В предыдущей статье защита данных – восстановление мы рассмотрели принципы транзакций, контрольных точек, восстановление носителей, двухфазной фиксации и зеркального копирования. Теперь настало время рассказать о принципах параллелизма.
Параллелизм – возможность параллельной обработки в СУБД многих параллельных транзакций к одним и тем же данным в одно и то же время.
СУБД должно осуществлять правильное восстановление данных при отмене транзакций или в случае системного сбоя и гарантировать, что пользователи при параллельной работе с данными мешать друг другу не будут.
Основные проблемы, возникающие при параллельной обработке транзакций:
1. Потеря результатов обновления.
Возникает когда одновременно, но с некоторым сдвигом во времени выполняются 2 транзакции, заключающиеся в чтении данных из одного и того же кортежа с последующим изменением данных в них.
Если 2 транзакции одновременно изменяют и фиксируют изменения одних и тех же данных, то сохраняются результаты той транзакции, которая завершилась последней по времени.
2. Незафиксированные зависимости, или т.н. «грязное чтение» (Dirty Read).
Появляется, если с помощью некой транзакции Т2 осуществляется чтение или изменение некоторого кортежа, только что измененного другой транзакцией Т1,однако изменения еще не были подтверждены. Если данные транзакции Т1 подтверждены не будут, то результаты транзакции Т2 окажутся неверными.
3. Несогласованные данные (неповторяемое чтение Fuzzy Read).
Возникает, когда одна транзакция читает кортеж, а другая изменяет его и фиксирует изменения до окончания первой.
4. Строки-призраки (Phantom).Возникает при работе не с одним кортежем, а при выполнении групповых операции (вычисление среднего, подсчет баланса, т. е. когда обрабатывается несколько строк одновременно). В таких сложных обработках, как правило,некий массив кортежей читается неоднократно. Если между первым и вторым чтениями была удалена или добавлена запись в массив строк, при повторном считывании массива появляется лишняя строка (фантом), что может привести к неверным результатам.
Т.о. СУБД должна гарантировать, что если 2 транзакции выполняются параллельно, то результаты их выполнения должны быть аналогичны тем, которые были бы при их последовательном выполнении, каждый пользователь должен чувствовать себя так, как будто он работает монопольно (концепция сериализации транзакций).
Для решения проблем при параллельной работе используется механизм блокировок: одна или набор записей блокируются на момент выполнения транзакции. Поэтому желательно делать транзакции покороче, чтобы обеспечить равномерную работу пользователей в сети.
На практике используются 2 вида блокировок:
— без взаимного доступа (монопольная, exclusive locks)
— с взаимным доступом (разделяемая, shared locks)
Транзакция, предназначенная для чтения, извлечения записи, должна накладывать s-locks на читаемые записи. Транзакция, предназначенная для изменения данных, должна накладывать х-locks на эти записи. Если этой же транзакцией до этого была наложена s-locks,то эта блокировка поменяется на х-locks.Это приводит к тому, что в первом случае, только при чтении, другие пользователи могут одновременно читать ту же запись, а при изменении данных другим пользователям запрещено видеть неподтвержденные данные, пока они не будут зафиксированы.
Блокировки, как правило, задаются неявно:
запрос на извлечение данных (select) по умолчанию считается запросом с s-locks
любой запрос на обновление (insert, delete, update) по умолчанию х-locks
Некоторые СУБД поддерживают режим сосуществования s-locks,накладываемых разными клиентами на один и тот же ресурс. Т. к. если несколько человек одновременно читают одни и те же данные, то при отказе наложившего s-locks формально получается, что данные уже можно изменять, хотя другие пользователи продолжают чтение.
Для осуществления s-locks и х-locks системе нужны ресурсы (память),которая отводится для установки защиты на те или иные кортежи. Обычно в СУБД возможное число одновременных блокировок – настраиваемая величина. Как только на одну и ту же таблицу набирается некоторое количество блокировок, система переводит ее из режима блокировки записей в режим блокировки всей таблицы. Это позволяет экономить ресурсы.
Для заданного набора транзакций любой порядок их выполнения называется графиком запуска. Если транзакции выполняются друг за другом, последовательно, то такой график запуска – последовательный.
Блокировки нарушают график запуска, что может приводить к разным результатам деятельности нескольких параллельно работающих пользователей. Для того, чтобы не нарушать последовательность транзакций, используют механизм упорядочения транзакций.
Во многих коммерческих СУБД на сегодня существуют и другие методы осуществления параллельной работы кроме блокировок:
1. Явные блокировки (SQL Server, SyBase) : в некоторые команды (напр. select) включены ключевые слова, которые явным образом позволяют блокировать кортежи, страницы, таблицы на этапе проектирования.
2. Уровни изоляции транзакций. Существуют специальные команды, с помощью которых можно информировать СУБД о том, что некоторая программа не будет повторно считывать данные в момент транзакции, позволяя тем самым СУБД снять блокировку до окончания транзакции.
3. Параметры блокировки. Администратор БД во многих СУБД, как правило, вручную имеет возможность менять размер блокируемого участка. Кроме того, он может изменять число блокировок, интервал и т.п.
1. Степень дробления блокировок
Блокировке может подвергаться не только отдельная запись, но и несколько записей вплоть до блокировки всей таблицы. Кроме того, возможна теоретически блокировка на уровне одного поля. На практике блокировка поля не используется из-за ресурсоемкости, а выигрыш во времени невелик. Чаще всего блокировки осуществляются на уровне страниц (1..8 кБ),т.к. в реляционных СУБД извлечение данных также ведется страницами.
Статистика:
На одну блокировку SQL Server тратит 32 байта, число блокировок можно установить от 5000 до 2 млрд.Обычно сервер настраивается таким образом, что при превышении некоторого числа блокировок на отдельные записи таблицы (регулируемо, обычно 200),то SQL Server автоматически переводит ресурс на блокировку всей таблицы.
2. Взаимные блокировки
Из-за того, что несколько пользователей могут по очереди подключаться и блокировать несколько записей в рамках одной транзакции, возможно возникновение тупиковых ситуаций: транзакция Т1 обработала одну запись и обратилась ко второй записи, а транзакция Т2 сначала обработала и заблокировала вторую запись и хочет обратиться к первой. Каждая из транзакций должна обратиться к следующей записи для обновления, а запись заблокирована.Транзакции не могут закончиться, что мешает работать и другим пользователям. В этом случае в системе предусмотрены специальные периодически подключающиеся сервисы, которые сканируют оперативную память, ищут взаимные блокировки и жертвуют одной транзакцией в пользу завершения другой.
Для этого используются алгоритмы:
— ожидание отмены. Предполагается, что старые транзакции отменяются в пользу новых
— отмена ожидания. Предполагает, что следует завершить старые транзакции.
Выявление взаимных блокировок осуществляется следующим образом: строится специальный граф ожидания .Каждая транзакция имеет свою вершину в этом графе. В случае возникновения блокировок от одной транзакции к другой проводится ребро, при взаимной блокировке появляется петля. Т. о. специальная программа, составляющая граф ожидания, ищет петли и, действуя по принципу ожидания отмены или отмены ожидания, отменяет одну из транзакций.
3. Уровни изоляции
Один из способов вместо блокировок – т.н. уровни изоляции. Термин «уровни изоляции» используется для описания степени вмешательства параллельных транзакций в работу некоторой данной.
Уровней изоляции может быть насколько. В стандарте SQL принято 4,в порядке возрастания приоритета:
1. Read Uncommitted – незавершенное чтение. Не имеет ограничений на чтение данных, все данные видны пользователям, обеспечивается максимальный параллелизм, минимальная изоляция, разрешается делать только одно изменение (одному из пользователей).Предотвращается только пропавшее обновление.
2. Read Committed – завершенное чтение. Гарантирует отсутствие пропавших обновлений и грязного чтения. Но также обеспечивает высокий уровень параллелизма и разрешает модифицировать несколько данных.
3. Repeatable read – повторяемое чтение. Ограничивает пропавшие обновления, грязное чтение, неповторяемое чтение. Приемлем в тех случаях, когда необходимы множественные изменения от нескольких клиентов. При этом автоматически устанавливаются блокировки на множество строк. Фактически каждая транзакция видит только первоначальную версию данных.
4. Serializable – Последовательное преобразование. Предотвращает все возможные проблемы параллельных транзакций, т. ч. и фантомы. Транзакции выполняются так, как будто они выполняются последовательно. На практике практически не встречается, по умолчанию чаще всего Read Committed.
4. Временные отметки
Метод временных отметок, применяемый для контроля целостности данных, полностью отличается от блокировок. Он не предусматривает какого-либо ожидания конфликтных транзакций, они просто отменяются и запускаются заново.
В его основе лежит понятие временной отметки – уникального идентификатора, включающего помимо прочего момент запуска транзакции с помощью системных часов, который и лежит в основе этой метки.
Если транзакция предпринимает попытку записи или чтения данных, то эта транзакция выполняется тогда, когда обновление было сделано более старой транзакцией. В противном случае она отменяется и перезапускается заново, чтобы ее временная отметка поменялась и те транзакции, которые еще не прошли, успели закончиться.
Базовый протокол упорядочивания транзакций включает следующие пункты:
1. Если более старая транзакция пытается прочитать данные, обновленные более новой, то она отменяется и перезапускается с новой временной отметкой.
2. Если более старая транзакция пытается изменить данные, прочитанные или обновленные более новой, то она тоже отменяется и перезапускается с новой временной отметкой.
Модификация этого протокола (правило Томаса, правило игнорирования устаревшей записи): Если более старая транзакция пытается изменить данные, обновленные более новой, то она отменяется.