|
По материалам статьи Andres Taylor Advanced
SQL Server Locking Я думал, что знаю SQL Server достаточно хорошо. Я использую этот продукт уже больше 6 лет, и мне нравится знать об используемых мною инструментах все. Когда я преподавал на курсах программирования SQL Server, я заметил, что в материалах Microsoft представлена таблица совместимости блокировок. Та же таблица была представлена и в MSDN. Рассматривая эту таблицу, я удивился - неужели здесь нет блокировки Intent Update? Это привело меня к исследованию блокировок. Эта статья и есть результат этого исследования. Я написал эту статью для определенного читателя - для того, кто понимает уровни изоляции, блокировки намерения, мертвые блокировки и уровни блокировок. Если вы недостаточно уверенно разбираетесь в этих областях, вам нужно сначала ознакомиться с ними перед чтением этой статьи.
Уровни блокировок Флаги трассировки блокировок Блокировки столбцов Расширенная таблица совместимости блокировок Заключение Краткая биография Я надеюсь, что я расширю ваше понимание блокировок в SQL
Server и, возможно, научу вас некоторым приемам, которые вы
сможете использовать во время программирования на SQL
Server. Я уверен, что вы слышали о мертвых блокировках, когда
системный процесс Х блокирует ресурс А и ждет, чтобы
заблокировать ресурс B, в то время как системный процесс Y
блокирует процесс B и ждет освобождения процесса А. Если не
слышали, то поищите дополнительную информацию об этом в
MSDN. Мне нравится иллюстрировать то, о чем я говорю, примерами кода. В окне QA я запускаю следующий пакет: Заметьте, что я открываю транзакцию, но не закрываю ее. Это
сделано для удержания блокировок. Результат SP_LOCK с нужными строками и столбцами показан ниже: Как и ожидалось, ключ, который мы блокируем, OrderId,
заблокирован update блокировкой. Странное значение в столбце
Resource (89003da47679) - это хэшированное значение нашего
ключа, 10633. SQL Server использует у себя хэш таблицу как
таблицу блокировки, и это то, что мы видим здесь. Region - это таблица-куча с уникальным некластерным первичным ключом по столбцу RegionId. Чтобы заполнить этот запрос, SQL Server просматривает индекс по RegionId и блокирует страницы и ключи индекса при помощи update блокировок. Наконец, он найдет строку, требующую изменения. Т.к. команда обновления не изменяет RegionId, то ей не нужно накладывать эксклюзивную блокировку на индекс. Результат из SP_LOCK показан ниже: Мы видим IX блокировку на таблице и X блокировку на RID
обновляемой строки. KEY блокировка наложена на индекс по
RegionId. Это понятно по столбцу Indid. Как видно, у нас все
еще сохраняется update блокировка на индексе. Это один из
немногих случаев, когда вы можете непосредственно увидеть
update блокировку. SQL Server имеет несколько типов блокировок, но он также
выбирает и разные уровни блокировок. Database (DB): Это блокировка сессии - т.е. она не относится ни к какой транзакции, а только к пользователю, подключенному к определенной базе данных. Это нужно, чтобы предотвратить удаление базы данных, когда к ней подключены один или более пользователей. Заметьте, что SQL Server знает, что базы данных master и tempdb не могут быть удалены, и не накладывает DB блокировку на эти базы данных. Table (TAB): Это самая грубая логическая блокировка, которую может использовать SQL Server. Часто вы обнаруживаете intent блокировки на этом уровне. (Не помните точно, что такое intent блокировки? Читайте дальше.) Extent (EXT): Эти блокировки не используются для блокирования логических строк, а используются, когда SQL Server создает новые таблицы или расширяет существующие, также вы можете их видеть, когда файл увеличивается в размере. Page (PAG): Когда SQL Server требуется заблокировать одновременно множество строк, а свободные слоты блокировок заканчиваются, то он может использовать страничные блокировки. Чаще всего вы будете видеть intent блокировки на этом уровне. До SQL Server версии 6.5 включительно это была самая лучшая блокировка. Key (KEY): Лучший уровень блокировки, возможный в SQL Server, вместе с RID блокировкой. KEY блокировки используются в индексах, а RID блокировки - в таблицах-кучах. Изучая в настоящее время поведение блокировок в SQL Server, я обнаружил, что SQL Server в большинстве случаев ценит конкурентность выше, чем скорость. Высокая конкурентность означает, что множество пользователей может работать одновременно. По возможности это достигается путем небольших блокировок, чтобы не блокировать без необходимости данные, нужные другим пользователям. С другой стороны, высокая скорость может быть достигнута при помощи больших блокировок, что быстрее, чем накладывание множества маленьких блокировок. SQL Server 2000 может также поднимать уровень ваших
блокировок, если он заметит, что вы блокируете все больше и
больше строк. В этом случае SQL Server применяет табличную
блокировку и удаляет все индивидуальные блокировки на
страницах, ключах и RID. Заметьте - поднятие уровня блокировки
приводит к установлению табличных блокировок. SQL Server не
повышает уровень RID/KEY блокировок до страничных
блокировок. Теперь, в другом окне, давайте проверим результат sp_lock. На моем компьютере я отследил 853 блокировки, относящиеся к соединению, на котором я запустил UPDATE. Таблица Orders в базе Northwind имеет 830 строк, так что я заблокировал каждую строку. Откатите транзакцию и попытайтесь снова применить update после создания индекса, как показано ниже. Теперь результат SP_LOCK показывает всего 25 блокировок. Эта часть производительности часто не замечается. Даже если вы используете READ COMMITTED, уровень изоляции по умолчанию, то вы все равно увидите большую разницу - снижение количества блокировок со 136 до 24 после создания индекса. Существует несколько флагов трассировки блокировок, которые
помогут настроить ваши блокировки и обнаружить проблемы с
мертвыми блокировками. 1200: Показывает все блокировки по всем соединениям.Очень объемный результат, я рекомендую использовать его в контролируемой среде, где работает только одно соединение. 1204: Показывает множество информации об участниках мертвой блокировки. Вот пример, улучшенный специально для вас. KEY: означает, что ресурс, заблокированный мертвой блокировкой, является ключом индекса. Этот ресурс также может быть любым другим типом блокировки, таким, как страница, RID, таблица, и т.д. ECID взят из master.dbo.sysprocesses. Он используется для различения блокировок, наложенных разными потоками. Режим - это режим блокировки, приведшей к мертвой блокировке, такой, как S, X или U. Строка "6:885578193:2" означает: база данных с Id 6, объект
с Id 885578193, и индекс с Id 2. Число в скобках - это
хэшированное значение, используемое, чтобы определить
блокировку. Вы можете увидеть это в столбце rsc_text таблицы
master.dbo.syslockinfo. К сожалению, это односторонний хэш,
что значит, что вы не можете найти заблокированную строку
только при помощи хэшированного значения. Spid - это, конечно,
системный процесс, наложивший блокировку. 1205: Выводит информацию о работе менеджера блокировок. Каждый раз, когда активизируется поиск мертвых блокировок, этот флаг трассировки заставляет менеджера блокировок выводить информацию о поиске. Работает, только если установлен флаг трассировки 1204. 1211: Отключает все поднятия уровня блокировок. Этот флаг трассировки не дает менеджеру блокировок поднимать уровень блокировок, даже если заканчиваются слоты блокировок. Как вы знаете, самой маленькой единицей блокировки в SQL
Server 2000 является блокировка строки. Блокировка столбцов
недоступна напрямую в SQL Server. Я покажу вам, как можно
использовать блокировку индекса для имитации блокировки уровня
столбца. Сейчас мы изменим значение RegionDescription у одной из строк при помощи простой команды update, как показано ниже: Чтобы выполнить этот запрос, SQL Server не может использовать индекс - индекс не покрывает столбец RegionDescription. Поэтому SQL Server использует полное сканирование таблицы, чтобы найти строку, требующую изменения. Когда он находит эту строку, он превращает update блокировку этой строки в эксклюзивную блокировку. Чтобы убедиться в этом, запустите SP_LOCK в другом окне Query Analyzer. Вы должны увидеть эксклюзивную RID блокировку на соответствующем объекте. В том же окне, в котором вы запустили SP_LOCK, вы можете выполнить следующий select: Если только вы не указали READPAST или не установили
уровень изоляции транзакции как read uncommitted, то вы будете
ждать, пока первое соединение не снимет блокировку. В этот раз вы ждать не будете. Если вы похожи на меня, то вы любите смотреть на план выполнения, и именно план выполнения объясняет, почему нам не пришлось ждать. Как видите, SQL Server выбрал сканирование индекса, чтобы
получить нужные данные для выполнения запроса. И т.к. вы
запросили информацию, которая может быть предоставлена чтением
индекса, то SQL Server'у не нужно читать данные из
таблицы-кучи. Этот тип запроса называется покрывающим
запросом. Расширенная таблица совместимости блокировок Доступные сейчас в BOL и MSDN таблицы не очень полны в плане совместимости различных блокировок. Из-за этого я включил в эту статью более полную таблицу. Думаю, вы найдете ее полезной. Я действительно нашел ускользающую intent update блокировку, и еще много чего, когда искал ее. Блокировки и поведение блокировок плохо документированы в BOL, что сделало это исследование гораздо более трудным, чем это должно было быть. Я узнал много нового во время написания это статьи, и надеюсь, что поделился частью этих знаний с вами. Andres Taylor пытается уделять одинаковое внимание его любимым занятиям: изучению SQL Server, Бразильскому Джиу-Джитсу и его красивой жене Мими. Он сдал 20 экзаменов MCP, является MCT и работает в Dotway в качестве консультанта по .NET/SQL Server. |
Перевод: Виталия Степаненко 2004г. |