Истории о данных: случай с нестандартными индексами
Проблема взаимоблокировок
Взаимоблокировки — это самое обычное явление, с которым практически ежедневно сталкиваются любые пользователи, работа которых так или иначе затрагивает оперирование параллельными базами. Естественно, существует ряд отлаженных процедур, направленных на то, чтобы свести взаимоблокировки к минимуму. Впрочем, полностью избавиться от них невозможно, если только не преобразовать форму доступа к базам данных из параллельной в последовательную посредством организации единого потока. При этом важно, чтобы приложения, предназначенные для разрешения ситуаций, в которых происходят взаимоблокировки, исключали возможность
Меня часто приглашают в программотехнические компании для оказания помощи в решении проблем блокировок в приложениях. Обычно специалисты говорят, что нуждаются в помощи в связи с блокировками, но неизменно выясняется, что у них происходят взаимоблокировки. Попытки снижения числа взаимоблокировок часто сводятся к сокращению времени, в течение которого осуществляются блокировки, и к уменьшению объема блокируемых данных. Мой опыт показывает, что заниматься решением проблемы блокировок нет смысла до тех пор, пока не подобраны оптимальные настройки для запросов. Когда запросы выполняются быстро, почти все связанные с блокировками неполадки исчезают.
Индексы и блокировки
Надлежащее индексирование — ключевое звено в деле решения всех проблем, связанных с блокировками. Важно сделать так, чтобы система SQL Server могла получить желаемый результат, блокируя минимальный объем ресурсов.
Поясню эту мысль на примере. Опубликованный в листинге код формирует таблицу, которую мы будем использовать при тестировании.
Если мы будем обновлять столбец PrimaryContact с помощью первичного ключа, объем блокируемых ресурсов будет минимальным:
BEGIN TRAN;
UPDATE dbo.Customers
SET PrimaryContact = N'Freddie Mercury'
WHERE CustomerID = 3;Я отметил, что мой сеанс — session_id 53, и могу теперь проверить (в другом окне запроса) блокировки, сохраняемые при использовании обновления (см. экран ниже).
SELECT resource_type, request_mode,
request_type, request_status
FROM sys.dm_tran_locks WHERE
request_session_id = 53;
Как и во всех случаях подключения к этой базе данных, мы сохраняем совместно используемую блокировку базы данных. Поскольку обновляется конкретный ключ, мы сохраняем намерение блокировок на самых высоких уровнях объекта, затем на уровне страницы и, наконец, мы сохраняем эксклюзивную блокировку на уровне ключа. Намерение блокировки представляет собой размещаемые на более высоком уровне указания на то, что инициатива блокировок зародилась на более низком уровне.
Теперь давайте создадим индекс, включающий столбец PrimaryContact, и добавим включенный столбец PhoneNumber:
CREATE INDEX
IX_dbo_Customers_PrimaryContact
ON dbo.Customers
(
PrimaryContact
)
INCLUDE
(
PhoneNumber
);
GOОбратите внимание на то, что если мы выполним обновление, не включающее ни один из указанных столбцов, ситуация с блокировками останется прежней (см. экран ниже).
BEGIN TRAN;
UPDATE dbo.Customers
SET IsReseller = 0
WHERE CustomerID = 3; Но если выполняемое нами обновление включает в себя хотя бы один из упомянутых столбцов индекса, блокировка усложняется, так как теперь нам приходится иметь дело и со страницами индексов (см. экран ниже).
BEGIN TRAN;
UPDATE dbo.Customers
SET PrimaryContact = N'Freddie Mercury'
WHERE CustomerID = 3;Отсюда следует, что само по себе добавление индекса с целью повысить производительность выполнения отчета оказывает влияние на типы сохраняемых блокировок и увеличивает вероятность возникновения проблем, связанных с блокировками или взаимоблокировками.