Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Nuove modifiche alla colonna solo metadati in SQL Server 2016

La ALTER TABLE ... ALTER COLUMN il comando è molto potente. Puoi usarlo per modificare il tipo di dati di una colonna, la lunghezza, la precisione, la scala, l'attribuzione di valori nulli, le regole di confronto... e molte altre cose.

È sicuramente più conveniente dell'alternativa:creare una nuova tabella e migrare i dati ogni volta che è necessaria una modifica. Tuttavia, c'è solo così tanto che si può fare per nascondere la complessità sottostante. Insieme a un gran numero di restrizioni su ciò che è anche possibile con questo comando, c'è sempre la questione delle prestazioni.

In definitiva, le tabelle vengono archiviate come una sequenza di byte con alcuni metadati in altre parti del sistema per descrivere il significato di ciascuno di questi byte e come si relazionano a ciascuna delle varie colonne della tabella. Quando chiediamo a SQL Server di modificare alcuni aspetti della definizione di una colonna, è necessario verificare che i dati esistenti siano compatibili con la nuova definizione. Deve anche determinare se è necessario modificare il layout fisico corrente.

A seconda del tipo di modifica e della configurazione del database, una ALTER COLUMN comando dovrà eseguire una delle seguenti azioni:

  1. Modifica solo i metadati nelle tabelle di sistema.
  2. Verifica la compatibilità di tutti i dati esistenti, quindi modifica i metadati.
  3. Riscrivi alcuni o tutti i dati memorizzati in modo che corrispondano alla nuova definizione.

L'opzione 1 rappresenta il caso ideale dal punto di vista delle prestazioni. Richiede solo poche modifiche alle tabelle di sistema e una quantità minima di registrazione. L'operazione richiederà comunque una modifica dello schema restrittiva Sch-M lock, ma le stesse modifiche ai metadati verranno completate molto rapidamente, indipendentemente dalle dimensioni della tabella.

Modifiche solo ai metadati

Esistono diversi casi speciali a cui prestare attenzione, ma in sintesi, le seguenti azioni richiedono solo modifiche ai metadati:

  • Partendo da NOT NULL a NULL per lo stesso tipo di dati.
  • Aumento della dimensione massima di un varchar , nvarchar o varbinary colonna (tranne per max ).

Miglioramenti in SQL Server 2016

L'oggetto di questo post sono le modifiche aggiuntive abilitate solo per i metadati da SQL Server 2016 in poi . Non sono necessarie modifiche alla sintassi e non è necessario modificare le impostazioni di configurazione. Ottieni questi miglioramenti non documentati gratuitamente.

Le nuove funzionalità hanno come target un sottoinsieme della lunghezza fissa tipi di dati. Le nuove abilità si applicano alle tabelle row-store nelle seguenti circostanze:

  • La compressione deve essere abilitata:
    • Su tutti gli indici e le partizioni , incluso l'heap di base o l'indice cluster.
    • O ROW o PAGE compressione.
    • Gli indici e le partizioni possono utilizzare una miscela di questi livelli di compressione. L'importante è che non ci siano indici o partizioni non compressi.
  • Passaggio da NULL a NOT NULL è non consentito .
  • Il seguente tipo di numero intero cambia sono supportati:
    • smallint a integer o bigint .
    • integer a bigint .
    • smallmoney a money (usa la rappresentazione intera internamente).
  • Le seguenti modifiche di tipo stringa e binario sono supportati:
    • char(n) a char(m) o varchar(m)
    • nchar(n) a nchar(m) o nvarchar(m)
    • binary(n) a binary(m) o varbinary(m)
    • Tutto quanto sopra solo per n < m e m != max
    • Le modifiche alle regole di confronto non sono consentite

Queste modifiche possono riguardare solo i metadati perché il layout dei dati binari sottostanti non cambia quando Descrittore di colonna viene utilizzato il formato riga (da cui la necessità di compressione). Senza compressione, l'archivio riga utilizza la FixedVar originale rappresentazione, che non può contenere queste modifiche al tipo di dati a lunghezza fissa senza riscrivere il layout fisico.

Potresti notare che tinyint viene omesso dall'elenco dei tipi interi. Questo perché non è firmato, mentre gli altri tipi interi sono tutti firmati, quindi non è possibile una modifica dei soli metadati. Ad esempio, un valore di 255 può essere contenuto in un byte per tinyint , ma richiede due byte in uno qualsiasi dei formati firmati. I formati firmati possono contenere da -128 a +127 in un byte quando compressi.

Esempio intero

Un'applicazione molto utile di questo miglioramento è la modifica del tipo di dati di una colonna con IDENTITY proprietà.

Supponiamo di avere la seguente tabella heap che utilizza la compressione di riga (funziona anche la compressione di pagina):

DROP TABLE IF EXISTS dbo.Test;
GO
CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL,
    some_value integer NOT NULL
)
WITH (DATA_COMPRESSION = ROW);

Aggiungiamo 5 milioni di righe di dati. Questo sarà sufficiente per rendere ovvio (dal punto di vista delle prestazioni) se la modifica del tipo di dati della colonna è un'operazione di soli metadati o meno:

WITH Numbers AS
(
    SELECT 
        n = ROW_NUMBER() OVER (ORDER BY @@SPID) 
    FROM sys.all_columns AS AC1
    CROSS JOIN sys.all_columns AS AC2
    ORDER BY n
    OFFSET 0 ROWS
    FETCH FIRST 5 * 1000 * 1000 ROWS ONLY
)
INSERT dbo.Test
    WITH (TABLOCKX)
(
    some_value
)
SELECT
    N.n
FROM Numbers AS N;

Successivamente eseguiremo il reseeding di IDENTITY per far sembrare che siamo quasi al punto di esaurire i valori che rientreranno in un integer :

DBCC CHECKIDENT
(
    N'dbo.Test',
    RESEED,
    2147483646
);

Possiamo aggiungere un'altra riga con successo:

INSERT dbo.Test
    (some_value)
VALUES
    (123456);

Ma tentando di aggiungere un'altra riga:

INSERT dbo.Test
    (some_value)
VALUES
    (7890);

Risulta in un messaggio di errore:

Msg 8115, livello 16, stato 1, riga 1
Errore di overflow aritmetico durante la conversione di IDENTITY in tipo di dati int.

Possiamo risolverlo convertendo la colonna in bigint :

ALTER TABLE dbo.Test
ALTER COLUMN id bigint NOT NULL;

Grazie ai miglioramenti di SQL Server 2016, questo comando cambia solo metadati , e si completa immediatamente. Il precedente INSERT istruzione (quella che ha generato l'errore di overflow aritmetico) ora viene completata correttamente.

Questa nuova capacità non risolve tutti i problemi relativi alla modifica del tipo di colonna con IDENTITY proprietà. Sarà comunque necessario eliminare e ricreare tutti gli indici sulla colonna, ricreare eventuali chiavi esterne di riferimento e così via. Questo è un po' fuori dallo scopo di questo post (sebbene Aaron Bertrand ne abbia già scritto in precedenza). Essere in grado di cambiare il tipo come operazione di soli metadati non fa certamente male. Con un'attenta pianificazione, gli altri passaggi necessari possono essere resi il più efficienti possibile, ad esempio utilizzando la registrazione minima o ONLINE operazioni.

Fai attenzione con la sintassi

Assicurati di sempre specifica NULL o NOT NULL quando si modificano i tipi di dati con ALTER COLUMN . Supponiamo, ad esempio, di voler modificare anche il tipo di dati di some_value colonna nella nostra tabella di test da integer NOT NULL a bigint NOT NULL .

Quando scriviamo il comando, omettiamo il NULL o NOT NULL qualificatore:

ALTER TABLE dbo.Test
ALTER COLUMN some_value bigint;

Questo comando viene completato correttamente come modifica dei soli metadati, ma rimuove anche il NOT NULL vincolo. La colonna ora è bigint NULL , che non è ciò che intendevamo. Questo comportamento è documentato, ma è facile ignorarlo.

Potremmo provare a correggere il nostro errore con:

ALTER TABLE dbo.Test
ALTER COLUMN some_value bigint NOT NULL;

Questo non una modifica dei soli metadati. Non siamo autorizzati a cambiare da NULL a NOT NULL (fare riferimento alla tabella precedente se è necessario un aggiornamento sulle condizioni). SQL Server dovrà controllare tutti i valori esistenti per assicurarsi che non siano presenti valori null. Quindi riscriverà fisicamente ogni riga della tavola. Oltre ad essere lente di per sé, queste azioni generano una grande quantità di log delle transazioni, che possono avere effetti a catena.

Come nota a margine, questo stesso errore non è possibile per le colonne con IDENTITY proprietà. Se scriviamo un ALTER COLUMN istruzione senza NULL o NOT NULL in tal caso, il motore presuppone utilmente che si intenda NOT NULL perché la proprietà identity non è consentita su colonne nullable. È comunque una buona idea non fare affidamento su questo comportamento.

Specifica sempre NULL o NOT NULL con ALTER COLUMN .

Fascicolazione

È necessario prestare particolare attenzione quando si modifica una colonna di stringa con regole di confronto che non corrispondono a quelle predefinite per il database.

Ad esempio, supponiamo di avere una tabella con regole di confronto che fanno distinzione tra maiuscole e minuscole e accenti (supponendo che il database predefinito sia diverso):

DROP TABLE IF EXISTS dbo.Test2;
GO
CREATE TABLE dbo.Test2
(
    id integer IDENTITY NOT NULL,
    some_string char(8) COLLATE Latin1_General_100_CS_AS NOT NULL
)
WITH (DATA_COMPRESSION = ROW);

Aggiungi 5 milioni di righe di dati:

WITH Numbers AS
(
    SELECT 
        n = ROW_NUMBER() OVER (ORDER BY @@SPID) 
    FROM sys.all_columns AS AC1
    CROSS JOIN sys.all_columns AS AC2
    ORDER BY n
    OFFSET 0 ROWS
    FETCH FIRST 5 * 1000 * 1000 ROWS ONLY
)
INSERT dbo.Test2
    WITH (TABLOCKX)
(
    some_string
)
SELECT
    CONVERT(char(8), N.n) COLLATE Latin1_General_100_CS_AS
FROM Numbers AS N;

Raddoppia la lunghezza della colonna della stringa usando il seguente comando:

ALTER TABLE dbo.Test2
ALTER COLUMN some_string char(16) NOT NULL;

Ci siamo ricordati di specificare NOT NULL , ma ho dimenticato le regole di confronto non predefinite. SQL Server presuppone che si intenda modificare le regole di confronto sul database predefinito (Latin1_General_CI_AS per il mio database di test). La modifica delle regole di confronto impedisce che l'operazione sia solo di metadati, quindi l'operazione viene eseguita per diversi minuti, generando cumuli di log.

Ricrea la tabella e i dati utilizzando lo script precedente, quindi prova ALTER COLUMN comando di nuovo, ma specificando le regole di confronto non predefinite esistenti come parte del comando:

ALTER TABLE dbo.Test2
ALTER COLUMN some_string 
    char(16) COLLATE Latin1_General_100_CS_AS NOT NULL;

La modifica ora viene completata immediatamente, come operazione di soli metadati. Come con il NULL e NOT NULL sintassi, vale la pena essere espliciti per evitare incidenti. Questo è un buon consiglio in generale, non solo per ALTER COLUMN .

Compressione

Tieni presente che la compressione deve essere specificata in modo esplicito per ogni indice e separatamente per la tabella di base se si tratta di un heap. Questo è un altro esempio in cui l'utilizzo di una sintassi abbreviata o di scorciatoie può impedire il risultato desiderato.

Ad esempio, la tabella seguente non specifica la compressione esplicita né per la chiave primaria né per la definizione dell'indice in linea:

CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL PRIMARY KEY,
    some_value integer NOT NULL
        INDEX [IX dbo.Test some_value]
)
WITH (DATA_COMPRESSION = PAGE);

La PRIMARY KEY avrà un nome assegnato, per impostazione predefinita CLUSTERED , ed essere PAGE compresso. L'indice in linea sarà NONCLUSTERED e per niente compresso. Questa tabella non sarà abilitata per nessuna delle nuove ottimizzazioni perché non tutti gli indici e le partizioni sono compressi.

Una definizione di tabella molto migliore e più esplicita sarebbe:

CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL
        CONSTRAINT [PK dbo.Test id]
        PRIMARY KEY CLUSTERED
        WITH (DATA_COMPRESSION = PAGE),
    some_value integer NOT NULL
        INDEX [IX dbo.Test some_value]
        NONCLUSTERED
        WITH (DATA_COMPRESSION = ROW)        
);

Questa tabella si qualificherà per le nuove ottimizzazioni perché tutti gli indici e le partizioni sono compressi. Come notato in precedenza, mescolare i tipi di compressione va bene.

Ci sono vari modi per scrivere questo CREATE TABLE dichiarazione in modo esplicito, quindi c'è un elemento di preferenza personale. L'importante punto da asporto è quello di essere sempre espliciti su quello che vuoi. Questo vale per CREATE INDEX separato anche dichiarazioni.

Eventi estesi e traccia flag

È presente un evento esteso specifico per il nuovo ALTER COLUMN solo per i metadati operazioni supportate da SQL Server 2016 in poi.

L'evento esteso è compressed_alter_column_is_md_only nel Debug canale. I suoi campi evento sono object_id , column_id e is_md_only (vero/falso).

Questo evento indica solo se un'operazione è solo metadati a causa delle nuove funzionalità di SQL Server 2016. Le modifiche alle colonne che erano solo metadati prima del 2016 mostreranno is_md_only = false nonostante sia ancora solo metadati.

Altri eventi estesi utili per monitorare ALTER COLUMN le operazioni includono metadata_ddl_alter_column e alter_column_event , entrambi nell'Analitica canale.

Se hai bisogno di disabilitare le nuove funzionalità di SQL Server 2016 per qualsiasi motivo, è possibile utilizzare il flag di traccia 3618 globale (o di avvio) non documentato. Questo flag di traccia non è efficace se utilizzato a livello di sessione. Non è possibile specificare un flag di traccia a livello di query con un ALTER COLUMN comando.

Pensieri finali

Essere in grado di modificare alcuni tipi di dati interi a lunghezza fissa con una modifica dei soli metadati è un miglioramento del prodotto molto gradito. Richiede che la tabella sia già completamente compressa, ma sta diventando comunque una cosa più comune. Ciò è particolarmente vero poiché la compressione è stata abilitata in tutte le edizioni a partire da SQL Server 2016 Service Pack 1.

Le colonne di tipo stringa a lunghezza fissa sono probabilmente molto meno comuni. Alcuni di questi potrebbero essere dovuti a considerazioni in qualche modo obsolete come l'utilizzo dello spazio. Quando compresse, le colonne di stringa a lunghezza fissa non memorizzano spazi finali, rendendole efficienti quanto le colonne di stringa a lunghezza variabile dal punto di vista dell'archiviazione. Può essere fastidioso tagliare gli spazi per la manipolazione o la visualizzazione, ma se i dati di solito occupano la maggior parte della lunghezza massima, i tipi a lunghezza fissa possono avere importanti vantaggi, non ultimo per quanto riguarda le concessioni di memoria per cose come l'ordinamento e l'hashing.

Non sono tutte buone notizie con la compressione abilitata. Ho accennato in precedenza che SQL Server a volte può eseguire una modifica dei soli metadati dopo aver verificato che tutti i valori esistenti verranno convertiti correttamente nel nuovo tipo. Questo è il caso quando si utilizza ALTER COLUMN per cambiare da integer a smallint Per esempio. Sfortunatamente, queste operazioni non sono attualmente solo metadati per oggetti compressi.

Ringraziamenti

Un ringraziamento speciale a Panagiotis Antonopoulos (Ingegnere software principale) e Mirek Sztajno (Senior Program Manager) dal team del prodotto SQL Server per la loro assistenza e guida durante la ricerca e la stesura di questo articolo.

Nessuno dei dettagli forniti in questo lavoro deve essere considerato come documentazione ufficiale Microsoft o dichiarazioni di prodotto.