Блокировки данных

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

Существует две основных блокировки – блокировка чтения и блокировка записи. Также их называют S-блокировка (от Shared, разделяемая) и X-блокировка (от eXclusive – исключительная).

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

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

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

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

Но такой подход может привести к следующей ситуации: допустим, имеется две таблицы и запускается две транзакции. Обе они должны изменить данные в обеих таблицах, но первая начинает с первой, а вторая – со второй. Соответственно, на обе таблицы устанавливается блокировка записи, и обе транзакции переходят в режим ожидания. Первая транзакция ждет освобождения второй таблицы, а вторая, соответственно, первой. Очевидно, что такое ожидание может длиться неограниченно долго. Такая ситуация называется взаимоблокировкой или «тупиком».

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

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

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

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

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

Здесь мы сталкиваемся с понятием «гранулярности блокировок», то есть возможности устанавливать блокировки разного масштаба. Гранулярность блокировок в разных СУБД может отличаться. Так, в Microsoft SQL Server имеются следующие блокировки:

Ресурс Описание
RID Идентификатор строки, используемый для блокировки одной строки в куче.
KEY Блокировка строки в индексе, используемая для защиты диапазонов значений ключа в сериализуемых транзакциях.
PAGE 8-килобайтовая (КБ) страница в базе данных, например страница данных или индекса.
EXTENT Упорядоченная группа из восьми страниц, например страниц данных или индекса.
HOBT Куча или сбалансированное дерево. Блокировка, защищающая индекс или кучу страниц данных в таблице, не имеющей кластеризованного индекса.
TABLE Таблица полностью, включая все данные и индексы.
FILE Файл базы данных.
APPLICATION Определяемый приложением ресурс.
METADATA Блокировки метаданных.
ALLOCATION_UNIT Единица размещения.
DATABASE База данных, полностью.

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

Для того, чтобы оптимизировать поиск блокировок были добавлены еще три так называемых блокировки намерения (intent). Это блокировка намерения чтения (IS), блокировка намерения записи (IX) и блокировка намерения чтения-записи (SIX). Эти блокировки нужны, чтобы выстроить иерархию блокировок и ускорить проверку доступности данных.

Например, если некоторая транзакция собирается прочитать несколько строк из таблицы, она сначала должна установить IS-блокировку на таблицу в целом, а потом уже устанавливать S-блокировку на эти строки (или страницы). В этом случае, транзакции, которым требуется X-блокировка, не должны проверять отдельные строки, а могут ограничиться проверкой блокировки намерения для все таблицы. При наличии IS-блокировки на таблицу установить X-блокировку не удастся.

То есть, блокировки намерения являются более «крупными», и во многих случаях позволяют быстрее понять, есть ли возможность заблокировать ресурс, не прибегая к просмотру более мелких блокировок.

Также при работе с блокировками намерения может применяться и так называемая эскалация блокировок. Так, если транзакция установила IS-блокировку на таблицу, а затем заблокировала много отдельных строк, то можно поднять IS-блокировку до S-блокировки таблицы в целом, а блокировки отдельных строк снять.

В разных СУБД могут иметься дополнительные блокировки (так, в Microsoft SQL Server есть еще блокировка обновления, U-блокировка, и соответствующие блокировки намерения), либо отсутствовать какие-то из рассмотренных. Соответственно, перед написанием программного кода необходимо выяснить, предоставляет ли СУБД требуемые возможности.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: