为什么简单的ALTER TABLE命令在带有全文索引的表上花费这么长时间?


14

我有一个很大的(约6700万行)名称/值表,该值在DataValue列上具有全文本索引。

如果我尝试运行以下命令:

ALTER TABLE VisitorData ADD NumericValue bit DEFAULT 0 NOT NULL;

它运行1小时10分钟,但仍无法在VisitorData包含约6700万行的表上完成。

  1. 为什么要花这么长时间而没有完成?
  2. 我该怎么办?

以下是有关表格的更多详细信息:

CREATE TABLE [dbo].[VisitorData](
            [VisitorID] [int] NOT NULL,
            [DataName] [varchar](80) NOT NULL,
            [DataValue] [nvarchar](3800) NOT NULL,
            [EncryptedDataValue] [varbinary](max) NULL,
            [VisitorDataID] [int] IDENTITY(1,1) NOT NULL, 
CONSTRAINT [PK_VisitorData_VisitorDataID] PRIMARY KEY CLUSTERED (
            [VisitorDataID] ASC
) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY], 
CONSTRAINT [UNQ_VisitorData_VisitorId_DataName] UNIQUE NONCLUSTERED (
            [VisitorID] ASC,
            [DataName] 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

ALTER TABLE [dbo].[VisitorData]
ADD  CONSTRAINT [UNQ_VisitorData_VisitorDataID] UNIQUE NONCLUSTERED (

[VisitorDataID] ASC
)
WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF,
      IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, 
      ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

ALTER TABLE [dbo].[VisitorData]
    WITH CHECK ADD
        CONSTRAINT [FK_VisitorData_Visitors] FOREIGN KEY([VisitorID])
        REFERENCES [dbo].[Visitors] ([VisitorID])
GO

ALTER TABLE [dbo].[VisitorData]
    CHECK CONSTRAINT [FK_VisitorData_Visitors] GO

CREATE FULLTEXT CATALOG DBName_VisitorData_Catalog WITH ACCENT_SENSITIVITY = ON
CREATE FULLTEXT INDEX ON VisitorData ( DataValue Language 1033 )
    KEY INDEX UNQ_VisitorData_VisitorDataID
    ON DBName_VisitorData_Catalog
    WITH CHANGE_TRACKING AUTO
GO

根据以下查询结果,ALTER TABLE命令执行期间发生的等待类型为LCK_M_SCH_M(模式修改):

select * from  sys.dm_os_waiting_tasks

waiting_task_address    session_id exec_context_id wait_duration_ms     wait_type            resource_address       blocking_task_address   blocking_session_id blocking_exec_context_id resource_description
--------------------             ----------     --------------- --------------------              -------------------- ------------------             ---------------------            -------------------        ------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0x0000000000B885C8   54               0                   112695                            LCK_M_SCH_M   0x00000000802DF600 0x000000000054E478     25                            0                                         objectlock lockPartition=0 objid=834102012 subresource=FULL dbid=5 id=lock438a02e80 mode=IS associatedObjectId=834102012
0x0000000000B885C8   54               0                   112695                            LCK_M_SCH_M   0x00000000802DF600 0x00000000088AB048    23                            0                                         objectlock lockPartition=0 objid=834102012 subresource=FULL dbid=5 id=lock438a02e80 mode=IS associatedObjectId=834102012

我正在使用运行SQL Server 2005 SP 2(即将升级到2008 SP2)的生产服务器。

Answers:


16

模式更改花费了很长时间,因为您要在更改过程中为该列分配默认值,并使用不可为空的列强制执行该默认值,并且该列必须填充60+百万行,这是非常昂贵的操作。我不确定您的应用程序要求是什么,但是可以使模式更改更快的一种方法是将其添加为没有默认值的可空列,然后分批执行更新以将0分配为该列的值。更新完成后,您可以应用另一个架构更改,以将列更改为不可为空,并分配默认值。


9

全文索引可能与您的问题无关。在SQL Server 2012之前,它ADD COLUMN NOT NULL DEFAULT ...是一种脱机操作,必须运行更新并用新添加的列的新默认值填充每一行。在SQL Server 2012+中,该操作要快得多,请参阅SQL Server 11中添加的带有值的在线非NULL列,因为它仅更新表的元数据,而实际上不更新任何行。

ALTER TABLE的更新很可能很慢。请记住,由于这是单个事务,因此将生成巨大的日志,并且您的日志现在可能会增长,并且随着日志的扩展会不断地归零。但是,由于一般的争用,它也可能很慢:该语句可能无法获取表上的SCH-M锁。查看sys.dm_exec_requests应该显示是否是这种情况,wait_typewait_resource列将指示该ALTER语句是否被阻止或正在执行。


0

最初由其作者添加到问题的答案:

根据Jason的回答,我改为发布了以下更新:

ALTER TABLE VisitorData ADD NumericValue bit NULL

最终确实执行了,但是花费了29分钟16秒。该操作本身应该非常快(仅用于元数据),因此我想几乎所有的时间都花在等待获取必要的LCK_M_SCH_M(模式修改)锁上。

有了新bit字段后,我可以通过脚本将默认值快速添加到其中:

ALTER TABLE VisitorData ADD
CONSTRAINT DF_VisitorData_NumericValue DEFAULT(0) FOR NumericValue;

我现在正在NumericValue使用用户定义的函数设置表中的所有位(请参见下文)。它正在进行中,在约6800万行的表中,每100万行大约需要1分钟。

WITH RD_CTE (VisitorD, DataName) 
AS
(
    SELECT TOP 10000 VisitorD, DataName
    FROM VisitorData WITH (NOLOCK)
    WHERE NumericValue IS NULL  
)
UPDATE VisitorData
SET NumericValue = CASE WHEN dbo.ufn_IsReallyNumeric(rd.DataValue) = 1 THEN 1 ELSE 0 END
FROM VisitorData rd WITH (NOLOCK) 
INNER JOIN RD_CTE rdc WITH (NOLOCK) ON rd.VisitorD = rdc.VisitorD  AND rd.DataName = rdc.DataName

GO 6800

完成后,我计划运行最终的模式调整,以使新的bit列不为空:

ALTER TABLE VisitorData ALTER COLUMN NumericValue bit NOT NULL;

希望,一旦所有值都不为空并且NumericValue默认值到位,此最后的架构更新将快速运行。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.