Justin Grant 的回答解释了 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 = ...) 语句添加到其脚本中,即使在不需要时也是如此。在需要此语句的情况下,添加它以保留表的当前设置,不锁定表在更改表时以某种特定方式schema这发生在那个脚本中。
换句话说,表被第一个ALTER TABLE ALTER COLUMN 语句上的SCH-M 锁锁定,而更改表模式的所有工作都已完成。最后一个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)
现在,如果我尝试在 SSMS 表设计器中更改 Col1 类型,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 操作,在源上都获得了锁
和目标表。对表所做的修改被记录并
完全可以恢复。影响非常大的所有行的更改
表,例如删除一列,或者在某些版本的 SQL Server 上,
添加具有默认值的 NOT NULL 列,可能需要很长时间
完成并生成许多日志记录。这些 ALTER TABLE 语句
执行时应与任何 INSERT、UPDATE 或 DELETE 一样小心
影响许多行的语句。