web-dev-qa-db-fra.com

Pourquoi la commande ALTER TABLE simple prend-elle autant de temps sur une table avec un index de texte intégral?

J'ai une grande table de nom-valeur (~ 67 millions de lignes) qui a une indexation en texte intégral sur la colonne DataValue.

Si j'essaie d'exécuter la commande suivante:

ALTER TABLE VisitorData ADD NumericValue bit DEFAULT 0 NOT NULL;

Il s'exécute pendant 1 heure 10 minutes et ne se termine toujours pas sur une table VisitorData qui contient ~ 67 millions de lignes.

  1. Pourquoi cela prend-il si longtemps et ne se termine-t-il pas?
  2. Que puis-je faire à ce sujet?

Voici plus de détails sur la table:

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

Les types d'attente qui se produisent pendant le ALTER TABLE commande sont LCK_M_SCH_M (modification de schéma), selon les résultats de la requête ci-dessous:

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

Je travaille avec des serveurs de production qui exécutent SQL Server 2005 SP 2 (qui sera bientôt mis à niveau vers 2008 SP2).

14
BobbyR-1of4

Le schéma le modifie si longtemps car vous attribuez une valeur par défaut à la colonne pendant la modification et l'imposez avec une colonne non nullable, et il doit remplir la colonne pour plus de 60 millions de lignes, ce qui est une opération incroyablement coûteuse. Je ne sais pas quelles sont les exigences de votre application, mais une approche qui rendrait le schéma plus rapide consiste à l'ajouter en tant que colonne nullable sans valeur par défaut, puis à effectuer une mise à jour par lots pour attribuer 0 comme valeur à la colonne. Une fois votre mise à jour terminée, vous pouvez appliquer une autre modification de schéma pour modifier la colonne en non nullable et attribuer la valeur par défaut.

16
Jason Cumberland

L'indexation de texte intégral n'est probablement pas pertinente pour votre problème. Avant SQL Server 2012, le ADD COLUMN NOT NULL DEFAULT ... est une opération hors ligne qui doit exécuter une mise à jour et remplir chaque ligne avec la nouvelle valeur par défaut de la colonne nouvellement ajoutée. Dans SQL Server 2012+, l'opération est beaucoup plus rapide, voir en ligne non NULL avec une colonne de valeurs ajoutée dans SQL Server 11 car il ne met à jour que les métadonnées de la table et ne met pas à jour les lignes.

Votre ALTER TABLE est très probablement lent à cause de la mise à jour. N'oubliez pas, puisqu'il s'agit d'une seule transaction, un énorme journal sera généré et votre journal est probablement en train de croître maintenant et d'être constamment mis à zéro à mesure qu'il se développe. Cependant, il peut également être lent en raison d'une contention ordinaire: l'instruction peut ne pas être en mesure d'acquérir le verrou SCH-M sur la table. Regarder sys.dm_exec_requests devrait indiquer si tel est le cas, le wait_type et wait_resource les colonnes indiqueraient si l'instruction ALTER est bloquée ou progresse.

9
Remus Rusanu

Réponse initialement ajoutée à la question par son auteur:

Selon réponse de Jason , j'ai plutôt publié la mise à jour suivante:

ALTER TABLE VisitorData ADD NumericValue bit NULL

Cela a finalement été exécuté, mais a pris 29 minutes et 16 secondes. L'opération elle-même devrait être assez rapide (métadonnées uniquement), donc j'imagine que presque tout ce temps a été passé à attendre pour acquérir le LCK_M_SCH_M (modification du schéma) verrou.

Avec le nouveau champ bit en place, j'ai pu y ajouter rapidement la valeur par défaut via le script:

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

Je suis en train de définir tous les bits NumericValue dans le tableau à l'aide d'une fonction définie par l'utilisateur (voir ci-dessous). Il est en cours et prend environ 1 minute par 1 million de lignes dans le tableau ~ 68 millions de lignes.

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

Une fois cela terminé, je prévois d'exécuter l'ajustement de schéma final pour rendre cette nouvelle colonne de bits non nulle:

ALTER TABLE VisitorData ALTER COLUMN NumericValue bit NOT NULL;

Espérons que cette dernière mise à jour du schéma s'exécutera rapidement une fois que toutes les valeurs seront non nulles et que la valeur par défaut NumericValue sera en place.

0
user126897