Укрупнение блокировки (компонент Database Engine)
Укрупнение блокировки — это процесс преобразования многих мелкогранулированных блокировок в меньшее число крупногранулированных блокировок при вероятном увеличении конкуренции параллелизма.
По мере того, как компонент Microsoft SQL Server Database Engine получает низкоуровневые блокировки, он устанавливает блокировки с намерением на объекты, содержащие нижележащие объекты:
- При блокировании строк или диапазонов ключей индекса компонент Database Engine помещает блокировку с намерением на страницы, содержащие эти строки или ключи.
- При блокировании страниц компонент Database Engine помещает блокировку с намерением на более высокий объектный уровень, содержащий эти страницы. Кроме выборочных блокировок на объекты (OBJECT), в таблице запрашиваются также выборочные блокировки кучи или сбалансированного дерева (HOBT) на:
- некластеризованный индекс, если страницы являются страницами некластеризованного индекса;
- кластеризованный индекс, если страницы являются страницами кластеризованного индекса. Это включает страницы данных в таблицах с кластеризованным индексом;
- куча страниц данных, если страницы являются страницами данных в таблице без кластерного индекса.
Компонент Database Engine может для одной и той же инструкции устанавливать блокировку как на строки, так и на страницы, чтобы свести к минимуму количество блокировок и исключить необходимость укрупнения этой блокировки. Например, компонент Database Engine может устанавливать блокировки страниц в некластеризованном индексе (если выбрано достаточное число последовательных ключей в узле индекса, чтобы удовлетворять условиям запроса) и блокировки строк в страницах данных.
Чтобы укрупнить блокировки, компонент Database Engine пытается изменить блокировку с намерением в таблице на соответствующую полную блокировку, например изменить блокировку с намерением монопольного доступа (IX) на монопольную (X) или блокировку с намерением совмещаемого доступа (IS) на совмещаемую (S). Если попытка укрупнения блокировки закончилась успешно и получена полная блокировка таблицы, то освобождаются все блокировки кучи или сбалансированного дерева, страниц (PAGE), диапазона ключей (KEY) или низкоуровневые блокировки (RID), которые удерживаются транзакцией на кучу или индекс. Если не удалось получить полную блокировку, в этот момент укрупнение блокировки не происходит и компонент Database Engine продолжит получать блокировки строк, ключей или страниц.
Компонент Database Engine не укрупняет блокировку строк или диапазона ключей до блокировки страниц, а повышает их прямо до блокировки таблиц. Точно так же блокировки страниц всегда укрупняются до блокировок таблиц.
Если попытка укрупнения блокировки заканчивается неудачей из-за конфликтующих блокировок, удерживаемых параллельными транзакциями, компонент Database Engine повторяет попытку для каждых дополнительных 1 250 блокировок, полученных транзакцией.
Каждое событие укрупнения происходит, прежде всего, на уровне одной инструкции компонента Transact-SQL. В начале события компонент Database Engine пытается укрупнить все блокировки, принадлежащие текущей транзакции, во всех таблицах, на которые ссылается активная инструкция, при условии, что она удовлетворяет требованиям порога повышения. Если событие укрупнения начинается до того, как инструкция получила доступ к таблице, попытки укрупнения блокировок для этой таблицы не предпринимаются. Если укрупнение блокировки прошло успешно, все блокировки, полученные транзакцией в предыдущей инструкции и все еще удерживаемые в момент начала события, повышаются, если на таблицу ссылается текущая транзакция и таблица включена в событие повышения.
Предположим, что сеанс выполняет следующие операции:
- Начинает транзакцию.
- Обновляет таблицу TableA. Этим формируются монопольные блокировки строк в таблице TableA, которые удерживаются до завершения транзакции.
- Обновляет таблицу TableB. Этим формируются монопольные блокировки строк в таблице TableB, которые удерживаются до завершения транзакции.
- Выполняет инструкцию SELECT, соединяющую таблицы TableA и TableC. План выполнения запроса предусматривает извлечение строк из таблицы TableA до извлечения строк из таблицы TableC.
- Инструкция SELECT вызывает укрупнение блокировки при извлечении строк из таблицы TableA до того, как она получает доступ к таблице TableC.
Если укрупнение блокировки прошло успешно, укрупняются только блокировки, удерживаемые сеансом на таблицу TableA. Они включают как совмещаемые блокировки инструкции SELECT, так и монопольные блокировки предыдущей инструкции UPDATE. В то время как для определения необходимости укрупнения учитываются только блокировки, которые сеанс получил для инструкции SELECT в TableA, после успешного укрупнения блокировок все блокировки, удерживаемые сеансом в таблице TableA, укрупняются до монопольной блокировки таблицы, а все другие блокировки с более низкой гранулярностью, включая блокировки с намерением в TableA, освобождаются.
Попытки укрупнить блокировки в таблице TableB не предпринимаются, поскольку в инструкции SELECT не было активных ссылок на таблицу TableB. Точно так же не предпринимались попытки укрупнить блокировки в таблице TableC, потому что к моменту укрупнения к ней не был получен доступ.
Пороги укрупнения блокировок
Укрупнение блокировок инициируется в одном из следующих случаев.
- Если одна инструкция Transact-SQL получает не менее 5 000 блокировок в одной таблице или индексе.
- Если количество блокировок в экземпляре компонента Database Engine превышает объем памяти или заданные пороги.
Если блокировки не могут быть укрупнены из-за конфликтов блокировок, компонент Database Engine периодически инициирует укрупнение блокировки при получении каждых 1 250 новых блокировок.
Пороги укрупнения для инструкции Transact-SQL
Укрупнение блокировки инициируется в случаях, когда Transact-SQL получает по крайней мере 5 000 блокировок на одну ссылку таблицы или на одну ссылку индекса, или, если таблица секционирована, то на одну ссылку секции таблицы или на одну ссылку секции индекса. Например, укрупнение блокировки не происходит, если инструкция получает 3 000 в одном индексе и 3 000 в другом в одной и той же таблице. Аналогичным образом, укрупнение блокировки не происходит, если инструкция имеет самосоединение, а каждая ссылка на таблицу получает только 3 000 блокировок.
Укрупнение блокировок выполняется только для таблиц, доступ к которым был получен после запуска повышения. Предположим, что инструкция SELECT представляет собой соединение, получающее доступ к трем таблицам в следующей последовательности: TableA, TableB и TableC. Эта инструкция получает 3 000 блокировок строк в кластеризованном индексе таблицы TableA и по крайней мере 5 000 блокировок строк в кластеризованном индексе таблицы TableB, но еще не получила доступ к таблице TableC. Если компонент Database Engine обнаруживает, что инструкция получила, по крайней мере, 5 000 блокировок строк в таблице TableB, он пытается укрупнить все блокировки, удерживаемые текущей транзакцией в таблице TableB. Этот компонент также пытается укрупнить все блокировки, удерживаемые текущей транзакцией в таблице TableA, но, поскольку число блокировок в TableA меньше 5 000, попытка закончится неудачей. В таблице TableC такие попытки не предпринимаются, поскольку к ней не был получен доступ ко времени укрупнения блокировок.
Порог укрупнения для экземпляра компонента Database Engine
Каждый раз, когда количество блокировок превышает порог памяти для укрупнения блокировки, компонент Database Engine инициирует укрупнение блокировки. Порог памяти зависит от параметра конфигурации locks:
- Если параметр locks имеет значение по умолчанию 0, порог укрупнения блокировок достигается, если память, используемая объектами блокировки, составляет 24 % от памяти компонента Database Engine, исключая память AWE. Длина структуры данных, которая используется для представления блокировки, равна примерно 100 байт. Этот порог динамический, поскольку компонент Database Engine динамически получает и освобождает память в целях компенсации меняющейся рабочей нагрузки.
- Если параметр locks имеет значение, отличное от 0, порог укрупнения блокировок составляет 40 процентов (или меньше, если памяти мало) от значения параметра locks.
Компонент Database Engine может выбирать для укрупнения любую активную инструкцию из сеанса, и для 1 250 новых блокировок он выбирает инструкции для повышения, если используемая блокировками память в экземпляре превышает порог повышения.
Укрупнение блокировок смешанного типа
Если происходит укрупнение блокировки, блокировка, выбранная для кучи или индекса, является достаточно сильной, чтобы удовлетворять требованиям самой ограничительной блокировки низкого уровня.
Предположим, что сеанс
- Начинает транзакцию.
- Обновляет таблицу, содержащую кластерный индекс.
- Выполняет инструкцию SELECT, которая ссылается на ту же таблицу.
Инструкция UPDATE получает следующие блокировки.
- Монопольные (X) блокировки на обновляемые строки данных.
- Блокировки с намерением монопольного доступа (IX) на страницы кластерного индекса, содержащего эти строки.
- Блокировку IX на кластерный индекс и еще одну на таблицу.
Инструкция SELECT получает следующие блокировки.
- Совмещаемые (S) блокировки на все считываемые строки данных, если они уже не защищены X-блокировкой инструкции UPDATE.
- Блокировки с намерением совмещаемого доступа на все страницы кластерного индекса, содержащего эти строки, если эти страницы уже не защищены IX-блокировкой.
- Не получает блокировки на кластерный индекс или таблицу, поскольку они уже защищены IX-блокировками.
Если инструкция SELECT получает достаточно блокировок для запуска укрупнения блокировок и оно проходит успешно, блокировка IX на таблицу преобразуется X-блокировку, а все блокировки строк, страниц и индексов освобождаются. Монопольными блокировками на таблицу защищаются все операции обновления и считывания.
Сокращение блокирования и укрупнения
В большинстве случаев компонент Database Engine обеспечивает оптимальную производительность при настройках по умолчанию для блокирования и укрупнения блокировок. Если экземпляру компонента Database Engine требуется большое количество блокировок и происходит частое их укрупнение, объем блокирования следует сократить следующим образом.
- Используя уровень изоляции, при котором не требуются совмещаемые блокировки для операций чтения.
- Уровень изоляции READ COMMITTED, если параметр базы данных READ_COMMITTED_SNAPSHOT включен (ON).
- Уровень изоляции SNAPSHOT.
- Уровень изоляции READ UNCOMMITTED. Это может использоваться только в системах с «грязным» чтением.
- Используя табличные подсказки PAGLOCK или TABLOCK, чтобы компонент Database Engine использовал блокировку страниц, кучи или индекса вместо блокировки строк. Однако при этом увеличивается вероятность блокирования пользователями других пользователей, которые пытаются получить доступ к тем же данным. Следует использовать только в системах с небольшим количеством пользователей.
Можно также использовать флажки трассировки 1211 и 1224, чтобы отключить все или некоторые укрупнения блокировок. Дополнительные сведения см. в разделе Флаги трассировки (Transact-SQL). Кроме того, отслеживание укрупнения блокировок с помощью события SQL Server Profiler Lock:Escalation см. в разделе Работа с приложением SQL Server Profiler.
См. также
Основные понятия
Уровни изоляции в ядре СУБД
Гранулярность блокировок и иерархии блокировок
Режимы блокировки
Совместимость блокировок (компонент Database Engine)
Параметр locks
Другие ресурсы
SET TRANSACTION ISOLATION LEVEL (Transact-SQL)
Табличная подсказка (Transact-SQL)
Архитектура таблиц и индексов