锁定升级 - 这里发生了什么?

时间:2009-11-09 20:34:44

标签: sql sql-server sql-server-2008

在SQL Server 2008中更改表(删除列)时,我单击了Generate Change Script按钮,我发现它生成的更改脚本会删除列,显示“go”,然后运行另一个ALTER TABLE语句,似乎将表的锁升级设置为“TABLE”。例如:

ALTER TABLE dbo.Contract SET (LOCK_ESCALATION = TABLE)

我还应该注意,这是更改脚本正在做的最后一件事。它在这做什么,为什么将LOCK_ESCALATION设置为TABLE?

3 个答案:

答案 0 :(得分:156)

Lock Escalation”是SQL处理大型更新锁定的方式。当SQL要改变很多行时,数据库引擎更有效地采用更少,更大的锁(例如整个表)而不是锁定许多较小的东西(例如行锁)。

但是当你有一个庞大的表时,这可能会有问题,因为对整个表进行锁定可能会长时间锁定其他查询。这是权衡:许多小粒度锁比较少(或一个)粗粒度锁慢,并且如果一个进程在另一个进程上等待,则有多个查询锁定表的不同部分会导致死锁的可能性。

SQL 2008中有一个表级选项LOCK_ESCALATION,它允许控制锁升级。默认情况下,“TABLE”允许锁一直升级到表级。在大多数情况下,DISABLE会阻止锁升级到整个表。 AUTO允许表锁,除非表是分区的,在这种情况下,锁仅限于分区级别。有关详细信息,请参阅this blog post

我怀疑IDE在重新创建表时添加了此设置,因为TABLE是SQL 2008中的默认设置。请注意,SQL 2005中不支持LOCK_ESCALATION,因此如果尝试运行,则需要将其删除2005年实例上的脚本。此外,由于TABLE是默认值,因此您可以在重新运行脚本时安全地删除该行。

另请注意,在此设置出现之前的SQL 2005中,所有锁都可以升级到表级别 - 换句话说,“TABLE”是SQL 2005上的唯一设置。

答案 1 :(得分:9)

您可以通过在运行脚本主要部分之前和之后比较此值来检查是否需要在脚本中包含LOCK_ESCALATION语句:

SELECT lock_escalation_desc FROM sys.tables WHERE name='yourtablename'

在我的情况下,更改要删除的表或添加约束似乎不会修改此值。

答案 2 :(得分:5)

贾斯汀·格兰特(Justin Grant)的回答说明了LOCK_ESCALATION设置的一般功能,但没有漏掉一个重要的细节,也没有说明SSMS为何生成设置它的代码。尤其是,LOCK_ESCALATION被设置为脚本中的最后一条语句看起来很奇怪。

我做了很少的测试,这是我对这里发生的事情的理解。

简短版

用于隐式添加,删除或更改列的ALTER TABLE语句将对表进行模式修改(SCH-M)锁定,这与表的LOCK_ESCALATION设置无关。 LOCK_ESCALATION影响DML语句(INSERTUPDATEDELETE等)中的锁定行为,而不影响DDL语句(ALTER)中的锁定行为。 SCH-M锁始终是整个数据库对象(在此示例中为表)的锁。

这很可能是造成混乱的地方。

SSMS在所有情况下都向其脚本中添加ALTER TABLE <TableName> SET (LOCK_ESCALATION = ...)语句,即使在不需要时也是如此。如果需要此语句,则会添加该语句以保留表的当前设置,不要在更改表 schema 时以某种特定方式锁定表发生在该脚本中。

换句话说,在完成所有更改表模式的工作时,表 用第一条ALTER TABLE ALTER COLUMN语句上的SCH-M锁锁定了。最后的ALTER TABLE SET LOCK_ESCALATION语句不影响它。它只会影响该表的将来的DML语句(INSERTUPDATEDELETE等)。

乍看起来,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这样做是为了保留表的当前设置。即使设置的当前值为默认的TABLE值,SSMS也会生成此行。我想是为了安全和明确起见,并防止将来可能发生的问题(如果将来此默认值更改)。这是有道理的。

在此示例中,确实需要生成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一样谨慎地执行   会影响很多行的语句。