On-Line Библиотека www.XServer.ru - учебники, книги, статьи, документация, нормативная литература.
       Главная         В избранное         Контакты        Карта сайта   
    Навигация XServer.ru






 

Блокировки в SQL Server 7.0

Кэйлен Дилани

Техника блокировок поднимается на новую высоту в версии 7.0

Одним из самых желанных новшеств версии 7.0 было введение блокирования данных на уровне строк. В данной статье рассмотрены принятые в SQL Server 7.0 модели блокировок, подлежащие блокированию ресурсы, а также простые средства наблюдения за активными блокировками.

Уровни изоляции и блокировок

Принятым по умолчанию уровнем изоляции в SQL Server является Зафиксированное чтение (Committed Read), но в приложении можно назначить вместо этого уровня другой. Самый прямолинейный способ сделать это - воспользоваться командой SET:

SET TRANSACTION ISOLATION LEVEL
        [READ UNCOMMITTED   I  READ COMMITTED   I
REPEATABLE  READ   I   SERIALIZABLE]

В предыдущих версиях SQL Server уровни изоляции Повторяемое чтение (Repeatable Read) и Упорядочиваемый (Serializable) воспринимались как синонимы, так как у системы не было механизмов, способных их различить. Предположим, что при работе в режиме Повторяемого чтения (Repeatable Read) был запущен на выполнение следующий запрос:

BEGIN TRANSACTION
SELECT * FROM titles
WHERE price BETWEEN $10 AND $15
GO

Режим Повторяемое чтение (Repeatable Read) обеспечивал отсутствие изменений в читаемых строках в процессе выполнения данной транзакции. Другими словами, режим Повторяемое чтение (Repeatable Read) гарантировал, что при повторном чтении будут получены те же самые результаты. SQL Server не мог заблокировать отдельные строки, которые отвечали введенному критерию, поэтому он блокировал страницу или даже всю таблицу. Подобная блокировка по существу устанавливала для рассматриваемой транзакции режим Упорядочиваемый (Serializable), поскольку блокировка страницы или таблицы не давала возможности другим процессам вставить строку, в которой стоимость равнялась бы, к примеру, 12 долларам.

В версии SQL Server 7.0 поддерживается настоящий уровень изоляции Повторяемое чтение (Repeatable Read). В действительности уровень Повторяемое чтение (Repeatable Read) не должен исключать подобные вставки; он предотвращает лишь изменение читаемых данных. Только уровень изоляции Упорядочиваемый (Serializable) не разрешает вводить новые строки в диапазон. Таким образом, реализация действительного уровня изоляции Повторяемое чтение (Repeatable Read) была невозможна до тех пор, пока не появились блокировки строк в версии SQL Server 7.0.

Режимы блокирования

Для достижения четырех требуемых по стандарту ANSI уровней изоляции в SQL Server реализованы режимы блокирования, отвечающие следующим видам блокировок: разделяемым, исключающим, намеренным и блокировкам обновления. Режим блокирования определяет, насколько ограничительной будет блокировка, и какие действия разрешено выполнять с заблокированными данными.

Разделяемые блокировки.

SQL Server автоматически запрашивает разделяемую блокировку при любом чтении данных. Таблица, страница, ключ индекса, отдельная строка - все эти объекты могут быть закрыты разделяемой блокировкой. В дополнение к этому SQL Server может накладывать блокировку на диапазоны ключей индекса. Это делается с целью реализации режима Упорядочиваемый (Serializable) при изоляции транзакций. Одновременно несколько процессов могут наложить разделяемую блокировку на одни и те же данные. Но ни один процесс не может потребовать исключающую блокировку для данных, которые закрыты разделяемой блокировкой. Исключение может быть сделано только для процесса, который наложил на эти данные текущую разделяемую блокировку. Разделяемая блокировка снимается сразу же, как только SQL Server прочтет информацию. Для того чтобы изменить такое поведение, следует использовать другие уровни изоляции транзакций - либо Упорядочиваемый (Serializable), либо Повторяемое чтение (Repeatable Read).

Исключающие блокировки.

SQL Server автоматически назначает исключающую блокировку, когда собирается изменить данные через команды Вставить (Insert), Обновить(Update) или Удалить(Delete). Наложить исключающую блокировку на определенный ресурс данных в каждый момент может только один процесс. Исключающая блокировка остается в силе до завершения транзакции. Поэтому изменяемые данные недоступны любым другим процессам до тех пор, пока текущая транзакция или успешно завершится, или выполнит откат. При использовании уровня изоляции транзакций Читать незафиксированное (Read Uncommitted) другие процессы могут прочитать данные, закрытые исключающей блокировкой.

Блокировки обновления.

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

Намеренные блокировки.

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

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

Степень детализации блокировок

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

В таблицу Syslockinfo записывается информация о типе заблокированного ресурса (это может быть, например, таблица, ключ или страница), о режиме блокирования и о конкретном идентификаторе ресурса. Тем самым каждая блокировка подробно отслеживается. Когда процесс запрашивает блокировку, SQL Server сравнивает предоставляемые при этом сведения с информацией о зафиксированных ранее в таблице Syslockinfo блокировках и ищет случаи точного совпадения типа и идентификатора ресурса. (Режимы блокировок могут различаться.) Предположим для примера, что один процесс наложил исключающую блокировку на строку в таблице Authors, а другой процесс пытается наложить блокировку на всю эту таблицу. Поскольку строки и таблицы относятся к разным типам ресурсов, SQL Server не обнаружит точного совпадения и не определит конфликт. Другое дело, если в таблице Syslockinfo содержится информация о намеренной блокировке, означающей, что строка таблицы уже находится в состоянии исключающей блокировки. Предположим далее, что процесс с исключающей блокировкой строки в таблице Authors, наложил намеренно исключающие блокировки на страницу и на таблицу, содержащие эту строку. В этой ситуации когда второй процесс сделает попытку объявить исключающую блокировку на таблицу Authors, система найдет в таблице Syslockinfo строку с блокировкой того же самого ресурса (таблицы Authors) и обнаружит конфликт.

Ключевые блокировки

SQL Server 7.0 поддерживает два вида блокировок ключей в зависимости от уровня изоляции текущей транзакции. Если установлены уровни изоляции Зафиксированное чтение (Committed Read) или Повторяемое чтение (Repeatable Read), то SQL Server делает попытку заблокировать ключи индекса, к которым он обращается при обработке запроса. Если таблица снабжена кластеризованным индексом, SQL Server запрашивает блокировку ключа, так как строки данных находятся на уровне листьев дерева индекса. Если же таблица представляет собой неупорядоченный массив (то есть не имеет кластеризованного индекса), SQL Server может запросить блокировку ключа для некластеризованного индекса и блокировку строк для данных.

Если установлен уровень изоляции Упорядочиваемый (Serializable), то наступает особая ситуация. Когда в ходе транзакции сканируется диапазон данных, необходимо закрыть блокировкой достаточную часть таблицы, чтобы гарантировать отсутствие каких-либо вставок новых значений в сканируемый диапазон. В противном случае при повторении запроса вставленное значение появится подобно призраку. Продемонстрируем это на следующем примере. Пусть имеется индекс по фамилиям служащих для таблицы Employee, работа происходит в режиме Упорядочиваемый (Serializable), и в текущей транзакции запускается такой оператор SELECT:

SELECT *
FROM employee
WHERE last_name BETWEEN 'MacDougall' AND 'McDougall'

Если MacAndrews, MacWorter и McKenna встречаются в дереве индекса таблицы на уровне листьев, то каждый из ключей MacWorter и McKenna требует объявления блокировки на диапазон значений. Блокировка диапазона ключа подразумевает блокирование всех записей, начиная с предшествующего значения ключа и заканчивая заблокированным ключом. Тогда никто не сможет вставить записи в этот диапазон. Так, в рассматриваемом нами примере один диапазон значений начинается с MacAndrews и заканчивается на MacWorter, а другой интервал начинается с ключа MacWorter и завершается значением McKenna. Эти два интервала значений ключа предотвращают вставку значений, превышающих MacAndrews и меньших или равных MacWorter, а также больших, чем MacWorter, но меньших или равных McKenna. Другими словами, эти два диапазона не дадут вставить записи для MacOwen или McBride, которые попадают в интервал значений, указанных в обороте WHERE. Однако при этом не удастся также вставить и значение MacBryde, хотя MacBryde и не удовлетворяет введенному условию. Конечно, блокировки диапазона ключа не совершенны. Но все же они позволяют в значительно большей степени распараллелить работу, чем при блокировке целиком страницы и тем более таблицы. А ведь в предшествующих версиях SQL Server существовали только такие блокировки.

Наблюдение за блокировками

Для того чтобы видеть одновременно и уже введенные системой на данный момент времени блокировки, и те, на которые только поступила заявка, можно либо просматривать таблицу Syslockinfo, либо запустить процедуру sp_lock. (Таблица Syslockinfo не является в полном смысле слова системной таблицей. SQL Server не поддерживает эту таблицу на диске, так как он не поддерживает блокировки на диске. Точнее будет сказать, что Syslockinfo появляется в формате таблицы каждый раз, когда процесс запрашивает Syslockinfo. При этом в ней находится информация о блокировках, полученная от диспетчера блокировок Lock Manager). Другой способ наблюдения за блокировками - воспользоваться прекрасным графическим представлением состояния блокировок, которое обеспечивает SQL Enterprise Manager.

Приводимые далее примеры демонстрируют, как применять процедуру sp_lock для всех типов блокировок при каждом уровне изоляции транзакций. Обратите внимание на то, что в коде sp_lock ключевое слово EXECUTE предшествует вызову процедуры sp_lock. Это ключевое слово требуется в тех случаях, когда вызов хранимой процедуры не является первым оператором в пакете. Заметьте также, что в качестве аргумента процедуры sp_lock использован параметр @@spid, который идентифицирует текущий процесс на сервере (server process ID). Это специально сделано для того, чтобы просматривать не все системные блокировки вообще, а только те, что относятся к вашему процессу.

Терминология, применяемая в выходных данных процедуры sp_lock способна привести к недоразумениям. Часто термины 'тип блокировки' и 'режим блокирования' используются как синонимы, они взаимно заменимы при определении того, является ли блокировка разделяемой или исключающей. Но в выходных данных процедуры sp_lock столбец Тип () содержит сведения о типе блокируемого ресурса, то есть о степени детализации блокировки. Значение для заблокированного ресурса может появляться в различных местах выходных данных процедуры sp_lock. Если заблокирована база данных целиком, то идентификатор базы данных появится в столбце Идентификатор базы данных (Dbid). Для заблокированной таблицы значение будет содержаться в столбце Идентификатор объекта (Objid). Идентификатор заблокированной страницы состоит из двух частей - идентификатора файла и идентификатора страницы. Он размещается в столбце Ресурс (Resource). Например, обозначение 1:123 относится к странице 123 из первого файла. Идентификатор заблокированной строки содержит три составляющие - идентификатор файла, идентификатор страницы и идентификатор сегмента на странице. Все эти компоненты размещаются в столбце Ресурс (Resource).

Блокировки баз данных при уровне изоляции Зафиксированное чтение (Read Committed). Каждый раз, когда запускается процедура sp_lock, она показывает выходные данные, относящиеся к базе данных master. Если взглянуть на столбец в результатах, приведенных на листинге 1, то увидим блокировки в базе данных 1 (Master) и в базе данных 2 (Tempdb). Кроме того, SQL Server запрашивает дополнительные блокировки базы данных уровня сессии в той базе данных, которая в данный момент является текущей. Этот уровень блокирования можно увидеть в третьей строке результатов, где идентификатор базы данных равен 5, а тип блокировки соответствует базе данных. В последующих примерах блокировки баз данных больше не будут приводиться. Отсутствие блокировок таблицы Authors в данный момент объясняется тем, что выполняемый пакет содержал лишь операторы SELECT, которые сопровождаются только разделяемыми блокировками. По умолчанию разделяемые блокировки снимаются, как только данные будут прочитаны, поэтому ко времени исполнения процедуры sp_lock все блокировки уже были отработаны.

Блокировки ключей и разделяемые блокировки при уровне изоляции Повторяемое чтение (Repeatable Read). В силу того что база данных Authors имеет кластеризованный индекс, все строки данных являются строками индекса на уровне листьев. Как показывает листинг 2, выходные данные представляют блокировки отдельных строк как блокировки ключа, а не блокировки записей. Но блокировки ключей также находятся на уровне листьев для не кластеризованных индексов таблиц. Различить эти два вида блокировок можно по значению, хранящемуся в столбце Indid: у строк данных оно равно 1, а у строк индекса это значение равно 2. (Ваши собственные не кластеризованные индексы могут получить любое значение от 2 до 250.) Поскольку уровень изоляции соответствует Повторяемому чтению (Repeatable Read), разделяемые блокировки сохраняются до окончания транзакции. Заметьте, что две строки данных и две строки индекса закрыты разделяемыми блокировками (S -shared), а блокировки страницы данных, страницы индекса и таблицы относятся к намеренно разделяемому типу (IS - intent-shared).

Блокировки диапазона ключа при уровне изоляции Упорядочиваемый (Serializable). Приведенные на листинге 3 блокировки, относящиеся к уровню изоляции Упорядочиваемый (), подобны блокировкам уровня Повторяемого чтения (), представленным на листинге 2. Основное различие заключается в режиме блокирования. Состоящее из двух частей обозначение IS-S определяет не только блокировку ключа, но и намеренную блокировку диапазона значений ключа. Первая часть (IS) относится к блокировке диапазона значений, начиная с предшествующего значения ключа в индексе и заканчивая заблокированным (S) значением (включительно). Построенному по столбцу c фамилией автора au_lname не кластеризованному индексу (Indid = 2) в таблице соответствуют три записи, так как SQL Server заблокировал три различных диапазона значений ключей.

Исключающие блокировки при уровне изоляции Зафиксированное чтение (Read Committed). Как показано на листинге 4, две строки уровня листьев кластеризованного индекса имеют исключающие блокировки (Х). Страница и таблица закрыты намеренно -исключающими блокировками (IX). Хотя SQL Server запрашивает блокировки обновления тогда, когда он ищет подлежащие обновлению строки, эти блокировки перерастают в исключающие блокировки еще до того, как операция обновления начнет выполняться. Ко времени исполнения процедуры sp_lock блокировки обновления уже завершились.

Блокировки строк при уровне изоляции Зафиксированное чтение (Read Committed). Упомянутая в листинге 5 таблица newTitles не содержит индексов, поэтому блокировка отвечающей критерию записи является исключающей блокировкой строки (RID). Как ожидалось, блокировки типа IX наложены на страницу и таблицу.

Подробнее о блокировках

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

Кэйлен Дилани Она работает независимым консультантом и преподавателем на северо-западе тихоокеанского побережья США. Она работает с SQL Server с 1987 года. Кэйлен написала книгу 'Внутри SQL Server 7.0' (Inside SQL Server 7.0), выпущенную издательством Microsoft Press; она также соавтор книг 'SQL Server 6.5 без границ' (SQL Server 6.5 Unleashed) и 'Изучи SQL Server за 21 день' (Teach yourself SQL Server in 21 days), изданных в Sams Publishing.


Листинг 1. Блокировки в базе данных при уровне изоляции Зафиксированное чтение (Committed Read)

Выходные данные процедуры sp_lock:
/Заголовки столбцов таблицы/
Spid - идентификатор процесса
Dbid - идентификатор базы данных
Objid - идентификатор объекта
Indid - индивидуальный идентификатор
Тип - тип ресурса (степень детализации)
Ресурс - идентификатор ресурса
Режим - режим блокирования
Статус - статус блокировки

	USE PUBS
	SET TRANSACTION ISOLATION LEVEL READ COMMITTED
	BEGIN TRAN
	SELECT * FROM author
	WHERE au_lname = `Ringer`
	EXEC sp_lock @@spid
	COMMIT TRAN

Листинг 2. Блокировки ключей и разделяемые блокировки при уровне изоляции Повторяемое чтение (Repeatable Read)

Выходные данные процедуры sp_lock:
/Заголовки столбцов таблицы/
Spid - идентификатор процесса
Dbid - идентификатор базы данных
Objid - идентификатор объекта
Indid - индивидуальный идентификатор
Тип - тип ресурса (степень детализации)
Ресурс - идентификатор ресурса
Режим - режим блокирования
Статус - статус блокировки

	USE PUBS
	SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
	BEGIN TRAN
	SELECT * FROM authors
	WHERE au_lname = `Ringer`
	EXEC sp_lock @@spid
	COMMIT TRAN

Листинг 3. Блокировки диапазона ключа при уровне изоляции Упорядочиваемый (Serializable)

Выходные данные процедуры sp_lock:
/Заголовки столбцов таблицы/
Spid - идентификатор процесса
Dbid - идентификатор базы данных
Objid - идентификатор объекта
Indid - индивидуальный идентификатор
Тип - тип ресурса (степень детализации)
Ресурс - идентификатор ресурса
Режим - режим блокирования
Статус - статус блокировки

	USE PUBS
	SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
	BEGIN TRAN
	SELECT * FROM authors
	WHERE au_lname = `Ringer`
	EXEC sp_lock @@spid
	COMMIT TRAN

Листинг 4. Исключающие блокировки при уровне изоляции Зафиксированное чтение (Read Committed)

Выходные данные процедуры sp_lock:
/Заголовки столбцов таблицы/
Spid - идентификатор процесса
Dbid - идентификатор базы данных
Objid - идентификатор объекта
Indid - индивидуальный идентификатор
Тип - тип ресурса (степень детализации)
Ресурс - идентификатор ресурса
Режим - режим блокирования
Статус - статус блокировки

	USE PUBS
	SET TRANSACTION ISOLATION LEVEL READ COMMITTED
	BEGIN TRAN
	UPDATE authors
	SET contract = 0
	WHERE au_lname = `Ringer`
	EXEC sp_lock @@spid
	COMMIT TRAN

Листинг 5. Блокировки строк при уровне изоляции Зафиксированное чтение (Read Committed)

Выходные данные процедуры sp_lock:
/Заголовки столбцов таблицы/
Spid - идентификатор процесса
Dbid - идентификатор базы данных
Objid - идентификатор объекта
Indid - индивидуальный идентификатор
Тип - тип ресурса (степень детализации)
Ресурс - идентификатор ресурса
Режим - режим блокирования
Статус - статус блокировки

SQL Batch:
	USE PUBS
	SET TRANSACTION ISOLATION LEVEL READ COMMITTED
	BEGIN TRAN
	update newTitles
	SET price = 3.99
	WHERE type = `business`
	EXEC sp_lock @@spid
	ROLLBACK TRAN


Литература по SQL Server