Відповідь Джастіна Гранта пояснює, що таке LOCK_ESCALATION
налаштування робить взагалі, але пропускає одну важливу деталь і не пояснює, чому SSMS генерує код, який його встановлює. Тим більше, дуже дивно виглядає те, що LOCK_ESCALATION
в сценарії встановлено останню заяву.
Я зробив кілька тестів і ось моє розуміння того, що тут відбувається.
Коротка версія
ALTER TABLE
Затвердження , яке додає, видаляє або змінює стовпець неявно приймає схему зміни (SCH-M) блокування таблиці, яка не має нічого спільного з LOCK_ESCALATION
установкою столу. LOCK_ESCALATION
впливає поведінка блокування під час заяв DML ( INSERT
, UPDATE
, DELETE
і т.д.), а не під час заяв DDL ( ALTER
). Замок SCH-M - це завжди блокування всього об’єкта бази даних, наведеного в цьому прикладі.
Ймовірно, звідки виходить плутанина.
SSMS додає ALTER TABLE <TableName> SET (LOCK_ESCALATION = ...)
заяву до свого сценарію у всіх випадках, навіть коли це не потрібно. У випадках, коли це твердження потрібно, воно додається для збереження поточного налаштування таблиці, а не для блокування таблиці якимось певним чином під час зміни схеми таблиці, що відбувається в цьому сценарії.
Іншими словами, таблиця буде заблокована з замком SCH-M на першому ALTER TABLE ALTER COLUMN
заяві в той час як вся робота зміни схеми таблиці робиться. Останнє ALTER TABLE SET LOCK_ESCALATION
твердження на це не впливає. Це впливає тільки на заяви майбутнього DML ( INSERT
, UPDATE
, DELETE
і т.д.) для цієї таблиці.
На перший погляд це виглядає так, ніби SET LOCK_ESCALATION = TABLE
має щось спільне з тим, що ми змінюємо всю таблицю (ми змінюємо її схему тут), але це вводить в оману.
Довга версія
Під час зміни таблиці в деяких випадках SSMS генерує сценарій, який заново створює всю таблицю, а в деяких більш простих випадках (наприклад, додавання або випадання стовпця) сценарій не створює повторно таблицю.
Візьмемо для прикладу цю зразкову таблицю:
CREATE TABLE [dbo].[Test](
[ID] [int] NOT NULL,
[Col1] [nvarchar](50) NOT NULL,
[Col2] [int] NOT NULL,
CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
У кожній таблиці є LOCK_ESCALATION
налаштування, яке встановлено TABLE
за замовчуванням. Давайте змінимо тут:
ALTER TABLE dbo.Test SET (LOCK_ESCALATION = DISABLE)
Тепер, якщо я спробую змінити Col1
тип в дизайнері таблиць SSMS, SSMS генерує сценарій, який заново створює всю таблицю:
BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Tmp_Test
(
ID int NOT NULL,
Col1 nvarchar(10) NOT NULL,
Col2 int NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE dbo.Tmp_Test SET (LOCK_ESCALATION = DISABLE)
GO
IF EXISTS(SELECT * FROM dbo.Test)
EXEC('INSERT INTO dbo.Tmp_Test (ID, Col1, Col2)
SELECT ID, CONVERT(nvarchar(10), Col1), Col2 FROM dbo.Test WITH (HOLDLOCK TABLOCKX)')
GO
DROP TABLE dbo.Test
GO
EXECUTE sp_rename N'dbo.Tmp_Test', N'Test', 'OBJECT'
GO
ALTER TABLE dbo.Test ADD CONSTRAINT
PK_Test PRIMARY KEY CLUSTERED
(
ID
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
COMMIT
Ви можете бачити вище, що він встановлює LOCK_ESCALATION
для новоствореної таблиці. SSMS робить це для збереження поточних налаштувань таблиці. SSMS генерує цей рядок, навіть якщо поточне значення параметра є TABLE
значенням за замовчуванням . Просто, щоб бути безпечним і явним і запобігти можливим майбутнім проблемам, якщо в майбутньому цей дефолт зміниться, я думаю. Це має сенс.
У цьому прикладі дійсно потрібно генерувати SET LOCK_ESCALATION
оператор, оскільки таблиця створюється заново і її параметр повинен бути збережений.
Якщо я спробую внести прості зміни в таблицю за допомогою дизайнера таблиць SSMS, наприклад додавання нового стовпця, то SSMS створює сценарій, який не створює повторну таблицю:
BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
ALTER TABLE dbo.Test ADD
NewCol nchar(10) NULL
GO
ALTER TABLE dbo.Test SET (LOCK_ESCALATION = DISABLE)
GO
COMMIT
Як бачите, він все-таки додає ALTER TABLE SET LOCK_ESCALATION
твердження, хоча в цьому випадку він взагалі не потрібен. Перший ALTER TABLE ... ADD
не змінює поточний параметр. Гадаю, розробники SSMS вирішили, що не варто намагатися визначити, у яких випадках ця ALTER TABLE SET LOCK_ESCALATION
заява є зайвою, і генерувати її завжди, просто щоб бути безпечною. Немає жодної шкоди щоразу додавати це твердження.
Знову ж таки, параметр для загальної таблиці LOCK_ESCALATION
не має значення, тоді як схема таблиці змінюється через ALTER TABLE
оператор. LOCK_ESCALATION
налаштування впливає лише на поведінку блокування в операторах DML, наприклад UPDATE
.
Нарешті, цитата ALTER TABLE
, підкресли мою:
Зміни, зазначені в ALTER TABLE, вводяться негайно. Якщо для змін потрібні зміни рядків у таблиці, ALTER TABLE оновлює рядки. ALTER TABLE отримує замок для зміни схеми (SCH-M) на столі, щоб переконатися, що під час зміни немає інших посилань на посилання навіть на метадані для таблиці., за винятком онлайнових операцій з індексом, які потребують дуже короткого блокування SCH-M в кінці. Під час операції «ALTER TABLE… SWITCH» блокування отримується як у вихідних, так і в цільових таблицях. Зміни, внесені до таблиці, записуються в систему та повністю підлягають відновленню. Зміни, що стосуються всіх рядків у дуже великих таблицях, наприклад, випадання стовпця або додавання стовпця NOT NULL зі значенням за замовчуванням у деяких виданнях SQL Server, може зайняти тривалий час для заповнення та генерації багатьох записів журналу. Ці оператори ALTER TABLE повинні виконуватися з такою ж обережністю, як і будь-які оператори INSERT, UPDATE або DELETE, які стосуються багатьох рядків.