Как влияет HOLDLOCK на UPDLOCK? - PullRequest
       17

Как влияет HOLDLOCK на UPDLOCK?

30 голосов
/ 06 октября 2009

Я видел много примеров использования подсказки HOLDLOCK в сочетании с UPDLOCK (, например ). Однако Документация Microsoft для этих подсказок создает впечатление, что HOLDLOCK должен быть избыточным, поскольку UPDLOCK уже сохраняет блокировку до конца транзакции. (Также кажется, что HOLDLOCK в любом случае применяется только к общим блокировкам.)

Как HOLDLOCK влияет на запрос, если вообще?

Ответы [ 2 ]

61 голосов
/ 06 октября 2009

Имеет большое влияние.

Блокировка обновления принимает блокировку обновления в строке, намеренное обновление на странице и общую блокировку таблицы / базы данных.

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

При использовании удержания запрос принудительно сериализуется, блокируя таблицу исключительно до завершения действия. Это не позволяет никому читать таблицу, если не используется подсказка nolock, что допускает потенциально грязное чтение.

Чтобы увидеть эффект, сгенерируйте пример таблицы 'foo' и поместите в нее некоторые мусорные данные.

begin tran

select * from foo with (updlock)
where tableid = 1
-- notice there is no commit tran

Откройте другое окно и попробуйте:

select * from foo

Строки возвращаются, теперь фиксируйте исходную транзакцию запроса. Перезапустите его, чтобы использовать Holdlock:

begin tran

select * from foo with (updlock, holdlock)
where tableid = 1

Вернитесь в другое окно и попробуйте выбрать данные еще раз, запрос не будет возвращать значения, поскольку он заблокирован исключительной блокировкой. Зафиксируйте транзакцию в первом окне, и появятся результаты для второго запроса, поскольку он больше не блокируется.

Последний тест - использование nolock, повторное выполнение транзакции с использованием updlock и holdlock. затем выполните следующее во втором окне:

select * from foo (nolock)

Результаты будут возвращаться автоматически, так как вы допустили риск грязного чтения (чтение незафиксировано).

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

Как и во всех функциях SQL, при правильном использовании они могут быть мощными, но неправильное использование функции / подсказки может вызвать серьезные проблемы. Я предпочитаю использовать подсказки в качестве крайней меры, когда мне нужно переопределить движок, а не в качестве подхода по умолчанию.

Редактировать как запрошено: протестировано в SQL 2005, 2008, 2008R2 (All Enterprise) - все установлено с настройками по умолчанию, тестовая база данных создана с использованием всех значений по умолчанию (только что введено только имя базы данных).

13 голосов
/ 19 ноября 2012

Ответ Эндрю является правильным в соответствии с документацией MSDN, однако я проверил по 2008R2 и 2012, и я не вижу такого поведения, поэтому, пожалуйста, проверьте себя

Я вижу следующее поведение:

Сначала запустите это в базе данных воспроизведения.

CREATE TABLE [dbo].[foo](
    [tableid] [int] IDENTITY(1,1) NOT NULL,
    [Col2] [varchar](100) NOT NULL,
    CONSTRAINT [PK_foo] PRIMARY KEY CLUSTERED 
    (
        [tableid] ASC
    )
)

... и вставьте несколько строк.

Теперь вставьте этот код в две вкладки запроса (измените текст «tab one» на вкладке 2):

begin tran

select * from foo with (UPDLOCK, HOLDLOCK)
where tableid = 1

UPDATE foo SET Col2 = 'tab one'
where tableid = 1

commit tran

И поместите это в другую вкладку 3 :

select * from foo
where tableid = 1
  1. Убедитесь, что вы указали на свою игровую базу данных, где находится стол.

  2. Выделите все ДО оператора обновления в на вкладке 1 и выполните.

  3. Сделайте то же самое на вкладке 2 , вы обнаружите, что вкладка 2 НЕ завершится и все еще выполняется.

  4. Теперь выполните простой SELECT на вкладке 3 , в моем окружении он завершается.

  5. Выделите оператор обновления в на вкладке 1 и выполните его (пока не выполняйте коммит), вы увидите, что вкладка 2 все еще выполняется.

  6. Продолжите и выполните коммит в tab 1 ... tab 2 теперь завершит выбор ... вы можете запустить остальные.

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