Por que o simples comando ALTER TABLE leva tanto tempo na tabela com índice de texto completo?

14

Eu tenho uma tabela de valor de nome grande (~ 67 milhões de linhas) que possui indexação de texto completo na DataValuecoluna.

Se eu tentar executar o seguinte comando:

ALTER TABLE VisitorData ADD NumericValue bit DEFAULT 0 NOT NULL;

Ele é executado por 1 hora e 10 minutos e ainda não é concluído em uma VisitorDatatabela que contém ~ 67 milhões de linhas.

  1. Por que isso está demorando tanto e não está sendo concluído?
  2. O que posso fazer sobre isso?

Aqui estão mais detalhes sobre a tabela:

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

Os tipos de espera que estão ocorrendo durante o ALTER TABLEcomando são LCK_M_SCH_M(modificação do esquema), conforme os resultados da consulta abaixo:

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

Estou trabalhando com servidores de produção que estão executando o SQL Server 2005 SP 2 (em breve a ser atualizado para o 2008 SP2).

BobbyR-1of4
fonte

Respostas:

15

O esquema muda isso demorando tanto tempo porque você está atribuindo um valor padrão à coluna durante a alteração e aplicando-o a uma coluna não anulável, e precisa preencher a coluna por mais de 60 milhões de linhas, o que é uma operação incrivelmente cara. Não sei ao certo quais são os requisitos de seu aplicativo, mas uma abordagem que tornaria o esquema mais rápido é adicioná-lo como uma coluna anulável sem valor padrão e, em seguida, executar uma atualização em lotes para atribuir 0 como o valor da coluna. Após a atualização, você poderá aplicar outra alteração de esquema para alterar a coluna para não anulável e atribuir o valor padrão.

Jason Cumberland
fonte
9

A indexação de texto completo é, provavelmente, irrelevante para o seu problema. Anterior ao SQL Server 2012, ADD COLUMN NOT NULL DEFAULT ...é uma operação offline que precisa executar uma atualização e preencher cada linha com o novo valor padrão da coluna recém-adicionada. No SQL Server 2012+, a operação é muito mais rápida, consulte a coluna Online não-NULL com valores no SQL Server 11 , pois atualiza apenas os metadados da tabela e, na verdade, não atualiza nenhuma linha.

ALTER TABLEProvavelmente você está lento por causa da atualização. Lembre-se, como essa é uma transação única, um log enorme será gerado e seu log provavelmente aumentará agora e será constantemente zerado à medida que se expande. No entanto, também pode ser lento devido a uma contenção comum: a instrução pode não conseguir adquirir o bloqueio SCH-M na tabela. Observando sys.dm_exec_requestsdeve mostrar se esse é o caso, as colunas wait_typee wait_resourceindicariam se a ALTERinstrução está bloqueada ou está progredindo.

Remus Rusanu
fonte
0

Resposta adicionada originalmente à pergunta por seu autor:

Conforme a resposta de Jason , emiti a seguinte atualização:

ALTER TABLE VisitorData ADD NumericValue bit NULL

Isso finalmente foi executado, mas levou 29 minutos, 16 segundos. A operação em si deve ser bem rápida (somente metadados), portanto, imagino que quase todo esse tempo foi gasto aguardando a aquisição do LCK_M_SCH_Mbloqueio necessário (modificação de esquema).

Com o novo bitcampo, pude adicionar rapidamente o valor padrão a ele através do script:

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

Agora estou definindo todos os NumericValuebits da tabela usando uma função definida pelo usuário (veja abaixo). Está em andamento e leva cerca de 1 minuto a cada 1 milhão de linhas na tabela ~ 68 milhões de linhas.

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

Quando terminar, pretendo executar o ajuste final do esquema para tornar a nova coluna de bits não nula:

ALTER TABLE VisitorData ALTER COLUMN NumericValue bit NOT NULL;

Felizmente, esta última atualização do esquema será executada rapidamente quando todos os valores forem nulos e o NumericValuepadrão estiver em vigor.

user126897
fonte