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

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

http://technet.microsoft.com/en-us/library/ms187373.aspx

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

Ответ 1

Это оказывает большое влияние.

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

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

Используя holdlock, запрос принудительно сериализуется, блокируя таблицу только до завершения действия. Это препятствует тому, чтобы кто-нибудь читал таблицу, если не используется подсказка 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 (все предприятия) - все установлено по довольно многим настройкам по умолчанию, тестовая база данных, созданная с использованием всех значений по умолчанию (просто введите имя только для базы данных).

Ответ 2

Ответ на Andrew верен в соответствии с документацией MSDN, однако я тестировал его с 2008 R2 и 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, вы увидите, что вкладка 2 НЕ будет завершена и все еще выполняется.

  • Теперь выполните простой SELECT в вкладке 3 в моей среде.

  • Выделите оператор update в вкладке 1 и выполните его (НЕ делайте фиксацию еще), вы увидите, что вкладка 2 выполняется STILL.

  • Идем дальше и выполняем фиксацию на вкладке 1... вкладка 2 теперь завершает выбор... вы можете запустить остальные.