¿Por qué el comando ALTER TABLE simple tarda tanto en la tabla con índice de texto completo?

14

Tengo una tabla de nombre-valor grande (~ 67 millones de filas) que tiene indexación de texto completo en la DataValuecolumna.

Si trato de ejecutar el siguiente comando:

ALTER TABLE VisitorData ADD NumericValue bit DEFAULT 0 NOT NULL;

Se ejecuta durante 1 hora y 10 minutos y aún no se completa en una VisitorDatatabla que contiene ~ 67 millones de filas.

  1. ¿Por qué lleva tanto tiempo y no se completa?
  2. ¿Qué puedo hacer al respecto?

Aquí hay más detalles sobre la tabla:

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

Los tipos de espera que se producen durante el ALTER TABLEcomando son LCK_M_SCH_M(modificación del esquema), según los resultados de la consulta a continuación:

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

Estoy trabajando con servidores de producción que ejecutan SQL Server 2005 SP 2 (que pronto se actualizará a 2008 SP2).

BobbyR-1of4
fuente

Respuestas:

15

El cambio del esquema lleva tanto tiempo porque está asignando un valor predeterminado a la columna durante el cambio y aplicando eso con una columna no anulable, y tiene que llenar la columna para más de 60 millones de filas, lo cual es una operación increíblemente costosa. No estoy seguro de cuáles son los requisitos de su aplicación, pero un enfoque que haría que el esquema cambie más rápido es agregarlo como una columna anulable sin valor predeterminado y luego realizar una actualización en lotes para asignar 0 como valor para la columna. Una vez que haya realizado la actualización, puede aplicar otro cambio de esquema para cambiar la columna a no anulable y asignar el valor predeterminado.

Jason Cumberland
fuente
9

La indexación de texto completo es, probablemente, irrelevante para su problema. Antes de SQL Server 2012, ADD COLUMN NOT NULL DEFAULT ...es una operación fuera de línea que debe ejecutar una actualización y llenar cada fila con el nuevo valor predeterminado de la columna recién agregada. En SQL Server 2012+, la operación es mucho más rápida; consulte la columna en línea No NULL con valores agregados en SQL Server 11, ya que solo actualiza los metadatos de la tabla y en realidad no actualiza ninguna fila.

Su ALTER TABLEmás probable es lento debido a la actualización. Recuerde, dado que es una transacción única, se generará un registro enorme y es probable que su registro crezca ahora y se reduzca constantemente a cero a medida que se expande. Sin embargo, también puede ser lento debido a la contención ordinaria: la declaración puede no ser capaz de adquirir el bloqueo SCH-M en la tabla. Mirando sys.dm_exec_requestsdebe mostrar si este es el caso, las wait_typey wait_resourcecolumnas indicarían si la ALTERdeclaración se bloquea o se está avanzando.

Remus Rusanu
fuente
0

Respuesta originalmente agregada a la pregunta por su autor:

Según la respuesta de Jason , emití la siguiente actualización:

ALTER TABLE VisitorData ADD NumericValue bit NULL

Esto finalmente se ejecutó, pero tardó 29 minutos, 16 segundos. La operación en sí misma debería ser bastante rápida (solo metadatos), así que imagino que casi todo ese tiempo se pasó esperando para adquirir el LCK_M_SCH_Mbloqueo necesario (modificación de esquema).

Con el nuevo bitcampo en su lugar, pude agregarle rápidamente el valor predeterminado a través del script:

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

Ahora estoy en el proceso de configurar todos los NumericValuebits en la tabla usando una función definida por el usuario (ver más abajo). Está en progreso y tarda aproximadamente 1 minuto por cada 1 millón de filas en la tabla de ~ 68 millones de filas.

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

Una vez que esté completo, planeo ejecutar el ajuste de esquema final para que la nueva columna de bits no sea nula:

ALTER TABLE VisitorData ALTER COLUMN NumericValue bit NOT NULL;

Con suerte, esta última actualización del esquema se ejecutará rápidamente una vez que todos los valores no sean nulos y el NumericValuevalor predeterminado esté en su lugar.

revs usuario126897
fuente