Warum dauert der einfache Befehl ALTER TABLE bei Tabellen mit Volltextindex so lange?

13

Ich habe eine große (~ 67 Millionen Zeilen) Name-Wert-Tabelle mit Volltextindizierung für die DataValueSpalte.

Wenn ich versuche, den folgenden Befehl auszuführen:

ALTER TABLE VisitorData ADD NumericValue bit DEFAULT 0 NOT NULL;

Es dauert 1 Stunde und 10 Minuten und wird in einer VisitorDataTabelle mit ~ 67 Millionen Zeilen immer noch nicht abgeschlossen .

  1. Warum dauert dies so lange und ist nicht abgeschlossen?
  2. Was kann ich tun?

Hier sind weitere Details zur Tabelle:

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

Die Wartetypen, die während des ALTER TABLEBefehls auftreten, lauten LCK_M_SCH_Mwie folgt (Schemaänderung):

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

Ich arbeite mit Produktionsservern, auf denen SQL Server 2005 SP 2 ausgeführt wird (in Kürze wird ein Upgrade auf 2008 SP2 durchgeführt).

BobbyR-1of4
quelle

Antworten:

14

Das Ändern des Schemas dauert so lange, da Sie der Spalte während der Änderung einen Standardwert zuweisen und diesen mit einer nicht nullwertfähigen Spalte erzwingen. Außerdem muss die Spalte mit mehr als 60 Millionen Zeilen gefüllt werden, was eine unglaublich teure Operation ist. Ich bin mir nicht sicher, welche Anwendungsanforderungen Sie haben, aber ein Ansatz, mit dem sich das Schema schneller ändern lässt, besteht darin, es als nullfähige Spalte ohne Standardwert hinzuzufügen und anschließend stapelweise zu aktualisieren, um 0 als Wert für die Spalte zuzuweisen. Nachdem Ihre Aktualisierung abgeschlossen ist, können Sie eine weitere Schemaänderung vornehmen, um die Spalte in nicht nullwertfähig zu ändern und den Standardwert zuzuweisen.

Jason Cumberland
quelle
9

Die Volltextindizierung spielt für Ihr Problem wahrscheinlich keine Rolle. Vor SQL Server 2012 ADD COLUMN NOT NULL DEFAULT ...ist dies ein Offline-Vorgang, bei dem ein Update ausgeführt und jede Zeile mit dem neuen Standardwert der neu hinzugefügten Spalte gefüllt werden muss. In SQL Server 2012+ ist der Vorgang viel schneller. Weitere Informationen finden Sie in SQL Server 11 unter Nicht-NULL-Online mit Spalten zum Hinzufügen von Werten, da nur die Metadaten der Tabelle aktualisiert werden und tatsächlich keine Zeilen aktualisiert werden.

ALTER TABLEAufgrund des Updates ist Ihr Computer wahrscheinlich langsam. Denken Sie daran, da es sich um eine einzelne Transaktion handelt, wird ein großes Protokoll generiert, und Ihr Protokoll wird wahrscheinlich jetzt größer und wird beim Erweitern ständig auf Null gesetzt. Es kann jedoch auch aufgrund von gewöhnlichen Konflikten langsam sein: Die Anweisung kann möglicherweise die SCH-M-Sperre für die Tabelle nicht abrufen. Wenn Sie sys.dm_exec_requestsnachsehen, ob dies der Fall ist, geben die Spalten wait_typeund wait_resourcean, ob die ALTERAnweisung blockiert ist oder Fortschritte macht.

Remus Rusanu
quelle
0

Antwort, die der Autor der Frage ursprünglich hinzugefügt hat:

Laut Jasons Antwort habe ich stattdessen das folgende Update veröffentlicht:

ALTER TABLE VisitorData ADD NumericValue bit NULL

Dies wurde schließlich ausgeführt, dauerte jedoch 29 Minuten und 16 Sekunden. Die Operation selbst sollte ziemlich schnell sein (nur Metadaten), daher stelle ich mir vor, dass fast die gesamte Zeit darauf verwendet wurde, die erforderliche LCK_M_SCH_MSperre (Schemaänderung) zu erwerben .

Mit dem neuen bitFeld konnte ich den Standardwert schnell über das Skript hinzufügen:

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

Ich bin gerade dabei, alle NumericValueBits in der Tabelle mit einer benutzerdefinierten Funktion zu setzen (siehe unten). Es ist in Bearbeitung und dauert ungefähr 1 Minute pro 1 Million Zeilen in der ~ 68-Millionen-Zeilentabelle.

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

Sobald dies abgeschlossen ist, plane ich, die endgültige Schemaanpassung durchzuführen, um diese neue Bitspalte auf einen anderen Wert als null zu setzen:

ALTER TABLE VisitorData ALTER COLUMN NumericValue bit NOT NULL;

Hoffentlich wird diese letzte Schemaaktualisierung schnell ausgeführt, wenn alle Werte nicht null sind und der NumericValueStandardwert vorhanden ist.

user126897
quelle