ALTER TABLE senza bloccare il tavolo?


107

Quando si esegue un'istruzione ALTER TABLE in MySQL, l'intera tabella è bloccata in lettura (consentendo letture simultanee, ma proibendo scritture simultanee) per la durata dell'istruzione. Se si tratta di una tabella grande, le istruzioni INSERT o UPDATE potrebbero essere bloccate per mooooolto tempo. C'è un modo per fare un "hot alter", come aggiungere una colonna in modo tale che la tabella sia ancora aggiornabile durante tutto il processo?

Per lo più sono interessato a una soluzione per MySQL, ma sarei interessato ad altri RDBMS se MySQL non può farlo.

Per chiarire, il mio scopo è semplicemente quello di evitare tempi di inattività quando una nuova funzionalità che richiede una colonna di tabella aggiuntiva viene inviata alla produzione. Qualsiasi schema di database si cambia nel corso del tempo, che è solo un fatto di vita. Non vedo perché dovremmo accettare che questi cambiamenti debbano inevitabilmente comportare tempi di inattività; è solo debole.


2
Ti chiedi quante volte cambierai il tavolo?
Allain Lalonde

1
IMHO, le modifiche allo schema del database sono associate a versioni completamente nuove: non vengono implementate sporadicamente come fanno le altre modifiche. È inevitabilmente un grosso problema.
dkretz

9
@AllainLalonde - più di 0 volte rende questa domanda legittima, soprattutto se i tempi di inattività nel tuo sistema costerebbero vite o molti soldi. E in ogni caso, a volte compaiono nuovi requisiti software.
Nathan Long,

Risposte:


60

L'unica altra opzione è fare manualmente ciò che molti sistemi RDBMS fanno comunque ...
- Crea una nuova tabella

È quindi possibile copiare il contenuto della vecchia tabella su un blocco alla volta. Mentre stai sempre attento a qualsiasi INSERT / UPDATE / DELETE sulla tabella di origine. (Potrebbe essere gestito da un trigger. Anche se ciò causerebbe un rallentamento, non è un blocco ...)

Al termine, cambia il nome della tabella di origine, quindi cambia il nome della nuova tabella. Preferibilmente in una transazione.

Una volta terminato, ricompilare eventuali stored procedure, ecc. Che utilizzano quella tabella. I piani di esecuzione probabilmente non saranno più validi.

MODIFICARE:

Alcuni commenti sono stati fatti su questa limitazione che è un po 'scarsa. Quindi ho pensato di dare una nuova prospettiva per mostrare perché è così ...

  • Aggiungere un nuovo campo è come cambiare un campo su ogni riga.
  • I blocchi di campo sarebbero molto più difficili dei blocchi di riga, per non parlare dei blocchi di tabella.

  • In realtà stai cambiando la struttura fisica sul disco, ogni record si sposta.
  • Questo è davvero come un AGGIORNAMENTO su tutta la tabella, ma con più impatto ...

2
E disponi di un piano di test approfondito prima dello scambio. Se fallisce, ricomincia.
dkretz

2
Gestire la sincronizzazione tramite trigger è stata una bella idea. Uso MySQL da così tanto tempo che continuo a dimenticare che ora hanno i trigger. Ho usato questa tecnica e ora ho uno script di modifica a caldo funzionale. Con una barra di avanzamento. E funziona con MyISAM. La vita è bella.
Daniel

2
+1 Questo è letteralmente ciò che fa SQL Enterprise Manager dietro le quinte quando si apportano determinati tipi di modifiche alle tabelle nell'interfaccia utente. In SQL 2008, hanno effettivamente aggiunto un avviso in modo che l'utente SAPPIA che sta eseguendo questa azione drastica.
BradC,

2
Non hai menzionato nulla sulle chiavi esterne che fanno riferimento alle tabelle che vengono modificate. Non sarebbe un problema?
Rafay

2
@MohammadRafayAleem - E i campi AUTOINCREMENT, le visualizzazioni e i trigger, ecc. Ecc. Ma anche così, l' approccio è ancora praticabile.
MatBailie

42

Percona crea uno strumento chiamato pt-online-schema-change che consente di farlo.

Essenzialmente fa una copia della tabella e modifica la nuova tabella. Per mantenere la nuova tabella sincronizzata con l'originale, utilizza i trigger per l'aggiornamento. Ciò consente di accedere alla tabella originale mentre la nuova tabella viene preparata in background.

Questo è simile al metodo suggerito da Dems sopra, ma lo fa in modo automatico.

Alcuni dei loro strumenti hanno una curva di apprendimento, vale a dire la connessione al database, ma una volta che l'hai giù, sono ottimi strumenti da avere.

Ex:

pt-online-schema-change --alter "ADD COLUMN c1 INT" D=db,t=numbers_are_friends

Sembra che il collegamento sia interrotto. Ho trovato questo collegamento funzionante.
Noam Ben Ari,

25

Questa domanda del 2009. Ora MySQL offre una soluzione:

DDL in linea (Data Definition Language)

Una funzionalità che migliora le prestazioni, la concorrenza e la disponibilità delle tabelle InnoDB durante le operazioni DDL (principalmente ALTER TABLE). Vedere Sezione 14.11, "InnoDB e DDL in linea" per i dettagli.

I dettagli variano a seconda del tipo di operazione. In alcuni casi, la tabella può essere modificata contemporaneamente mentre è in corso ALTER TABLE. L'operazione potrebbe essere eseguita senza eseguire una copia della tabella o utilizzando un tipo di copia della tabella appositamente ottimizzato. L'utilizzo dello spazio è controllato dall'opzione di configurazione innodb_online_alter_log_max_size.

Consente di regolare l'equilibrio tra prestazioni e concorrenza durante l'operazione DDL, scegliendo se bloccare completamente l'accesso alla tabella (LOCK = clausola EXCLUSIVE), consentire query ma non DML (LOCK = clausola SHARED) o consentire query complete e DML accesso alla tabella (clausola LOCK = NONE). Quando si omette la clausola LOCK o si specifica LOCK = DEFAULT, MySQL consente la massima concorrenza possibile a seconda del tipo di operazione.

L'esecuzione di modifiche sul posto ove possibile, invece di creare una nuova copia della tabella, evita aumenti temporanei nell'utilizzo dello spazio su disco e overhead di I / O associati alla copia della tabella e alla ricostruzione degli indici secondari.

vedere MySQL 5.6 Reference Manual -> InnoDB e Online DDL per maggiori informazioni.

Sembra che il DDL online sia disponibile anche in MariaDB

In alternativa è possibile utilizzare ALTER ONLINE TABLE per assicurarsi che ALTER TABLE non blocchi le operazioni simultanee (non accetta blocchi). È equivalente a LOCK = NONE.

MariaDB KB su ALTER TABLE


3
È un peccato che non ci sia altro che voti per portare questo in alto, dato che per lo più nega tutte le altre risposte semplicemente perché non fanno più riferimento alla versione corrente di MySQL.
Burhan Ali


14

Consiglio Postgres se è un'opzione. Con postgres sostanzialmente non ci sono tempi di fermo con le seguenti procedure:

Un'altra grande caratteristica è che la maggior parte delle istruzioni DDL sono transazionali, quindi è possibile eseguire un'intera migrazione all'interno di una transazione SQL e, se qualcosa va storto, l'intera operazione viene annullata.

Ho scritto questo un po 'di tempo fa, forse può fare un po' più chiaro sugli altri meriti.


6
Postgres crea ancora un blocco esclusivo sull'altare, impedendo ad altri di leggere da quella tabella.
clofresh

5
Non sono d'accordo con la parte "essenzialmente nessun tempo di inattività". Come ha detto clofresh, ALTER TABLE acquisisce un blocco esclusivo sulla tabella che blocca tutte le letture e le scritture simultanee. Nella mia esperienza, per i tavoli attivi la maggior parte delle volte non otterrai nemmeno il blocco (ALTER TABLE morirà di fame). E con le transazioni puoi facilmente finire con deadlock se non stai estremamente attento. Per questo motivo ora imposto sempre i tempi di inattività quando modifico le tabelle esistenti in Postgres.
Pankrat

1
una spiegazione più dettagliata: dba.stackexchange.com/questions/27153/… menziona le implicazioni del blocco esclusivo e alcuni modi per
aggirarlo

4
Sì, la modifica di una tabella in postgres acquisisce un blocco esclusivo, ma poiché l'operazione stessa viene completata in millisecondi, ciò è praticamente irrilevante nella maggior parte dei casi. Ho personalmente aggiunto colonne a tabelle da cento milioni di righe nel bel mezzo della giornata lavorativa con zero tempi di inattività.
Noah Yetter

2
@cobbzilla Sì, DROP COLUMN è altrettanto veloce. Sotto il cofano ciò che fondamentalmente fa è contrassegnare la colonna come nascosta. I valori che esistevano in quella colonna prima che venissero eliminati sono ancora nei file di dati (e visibili ad altre transazioni) e rimarranno tali a meno che e finché non si esegue un VACUUM FULL.
Noah Yetter

7

Dato che hai chiesto informazioni su altri database, ecco alcune informazioni su Oracle.

L'aggiunta di una colonna NULL a una tabella Oracle è un'operazione molto rapida poiché aggiorna solo il dizionario dei dati. Questo mantiene un blocco esclusivo sul tavolo per un brevissimo periodo di tempo. Tuttavia, invaliderà tutte le stored procedure, le viste, i trigger e così via. Questi verranno ricompilati automaticamente.

Da lì, se necessario, puoi creare l'indice utilizzando la clausola ONLINE. Anche in questo caso, solo i blocchi del dizionario dei dati molto brevi. Leggerà l'intera tabella alla ricerca di elementi da indicizzare, ma non blocca nessuno mentre lo fa.

Se devi aggiungere una chiave esterna, puoi farlo e fare in modo che Oracle si fidi che i dati sono corretti. Altrimenti ha bisogno di leggere l'intera tabella e convalidare tutti i valori che possono essere lenti (crea prima il tuo indice).

Se è necessario inserire un valore predefinito o calcolato in ogni riga della nuova colonna, sarà necessario eseguire un aggiornamento massiccio o forse un piccolo programma di utilità che popola i nuovi dati. Questo può essere lento, soprattutto se le righe diventano molto più grandi e non si adattano più ai loro blocchi. Il blocco può essere gestito durante questo processo. Dato che il vecchio versino della tua applicazione, che è ancora in esecuzione, non conosce questa colonna potresti aver bisogno di un subdolo trigger o per specificare un default.

Da lì, puoi eseguire uno scambio sui server delle applicazioni per la nuova versione del codice e continuerà a funzionare. Rilascia il tuo subdolo grilletto.

In alternativa, puoi usare DBMS_REDEFINITION che è una scatola nera progettata per fare questo genere di cose.

Tutto questo è così fastidioso da testare, ecc. Che abbiamo solo un'interruzione della domenica mattina presto ogni volta che rilasciamo una versione principale.


3

Se non puoi permetterti tempi di inattività per il tuo database durante gli aggiornamenti dell'applicazione, dovresti considerare di mantenere un cluster a due nodi per l'alta disponibilità. Con una semplice configurazione di replica, potresti apportare modifiche strutturali quasi completamente online come quella che suggerisci:

  • attendere che tutte le modifiche vengano replicate su uno slave passivo
  • cambiare lo slave passivo in modo che sia il master attivo
  • fare le modifiche strutturali al vecchio maestro
  • replicare le modifiche dal nuovo master al vecchio master
  • eseguire nuovamente lo scambio principale e la distribuzione della nuova app contemporaneamente

Non è sempre facile ma funziona, di solito con 0 tempi di inattività! Il secondo nodo non deve essere solo passivo, può essere utilizzato per test, fare statistiche o come nodo di fallback. Se non si dispone di infrastruttura, la replica può essere configurata all'interno di una singola macchina (con due istanze di MySQL).


1
Il vecchio maestro è fuori dal cluster o all'interno del cluster?
John Chornelius

2

No. Se stai usando le tabelle MyISAM, per quanto ne so, fanno solo blocchi di tabelle - non ci sono blocchi di record, cercano solo di mantenere tutto iperveloce attraverso la semplicità. (Altre tabelle MySQL funzionano in modo diverso.) In ogni caso, puoi copiare la tabella su un'altra tabella, modificarla e poi cambiarla, aggiornandola per differenze.

Questa è un'alterazione così massiccia che dubito che qualsiasi DBMS possa supportarla. È considerato un vantaggio in primo luogo essere in grado di farlo con i dati nella tabella.



Sì, MySQL è l'aberrazione. Ecco perché ero specifico sulle tabelle "standard".
dkretz

Hai scritto: le tabelle MySQL standard eseguono solo blocchi di tabelle, il che non è corretto.
Eran Galperin

Come interpreti questo riguardo alle tabelle MyISAM (cioè MySQL standard) dalla pagina che hai citato? "MySQL utilizza il blocco a livello di tabella per le tabelle MyISAM e MEMORY, il blocco a livello di pagina per le tabelle BDB e il blocco a livello di riga per le tabelle InnoDB."
dkretz

alcuni motori di archiviazione utilizzano il blocco a livello di riga e alcuni utilizzano il blocco a livello di tabella. Non esiste un motore di archiviazione standard (forse intendevi il valore predefinito in phpMyAdmin ...)
Eran Galperin,

2

Soluzione temporanea...

Un'altra soluzione potrebbe essere l'aggiunta di un'altra tabella con la chiave primaria della tabella originale, insieme alla nuova colonna.

Popolare la chiave primaria nella nuova tabella e popolare i valori per la nuova colonna nella nuova tabella e modificare la query per unirsi a questa tabella per le operazioni di selezione ed è inoltre necessario inserire, aggiornare separatamente per questo valore di colonna.

Quando sei in grado di ottenere tempi di inattività, puoi alterare la tabella originale, modificare le query DML e rilasciare la nuova tabella creata in precedenza

Altrimenti, potresti scegliere il metodo di clustering, la replica, lo strumento pt-online-schema di percona


1

Utilizzando il plugin Innodb, le istruzioni ALTER TABLE che aggiungono o rimuovono solo indici secondari possono essere eseguite "rapidamente", cioè senza ricostruire la tabella.

In generale, tuttavia, in MySQL, qualsiasi ALTER TABLE implica la ricostruzione dell'intera tabella che può richiedere molto tempo (cioè se la tabella contiene una quantità utile di dati).

È veramente necessario progettare la propria applicazione in modo che le istruzioni ALTER TABLE non debbano essere eseguite regolarmente; di certo non si desidera eseguire alcuna ALTER TABLE durante il normale funzionamento dell'applicazione a meno che non si sia pronti ad aspettare o si stiano modificando piccole tabelle.


1

Suggerirei uno dei due approcci:

  1. Progettare le tabelle del database tenendo conto dei potenziali cambiamenti. Ad esempio, ho lavorato con i sistemi di gestione dei contenuti, che modificano regolarmente i campi dati nel contenuto. Invece di costruire la struttura fisica del database in modo che corrisponda ai requisiti iniziali del campo CMS, è molto meglio creare una struttura flessibile. In questo caso, utilizzando un campo di testo BLOB (varchar (max) ad esempio) per contenere dati XML flessibili. Ciò rende i cambiamenti strutturali molto meno frequenti. Le modifiche strutturali possono essere costose, quindi anche qui c'è un vantaggio in termini di costi.

  2. Avere tempo per la manutenzione del sistema. Il sistema va offline durante le modifiche (mensili, ecc.) E le modifiche vengono pianificate durante l'ora del giorno meno trafficata (3-5am, ad esempio). Le modifiche vengono organizzate prima del lancio della produzione, quindi avrai una buona stima del tempo di inattività.

2a. Avere server ridondanti, in modo che quando il sistema ha tempi di inattività, l'intero sito non si interrompe. Ciò ti consentirebbe di "distribuire" i tuoi aggiornamenti in modo scaglionato, senza interrompere l'intero sito.

Le opzioni 2 e 2a potrebbero non essere realizzabili; tendono ad essere solo per siti / operazioni più grandi. Sono opzioni valide, tuttavia, e ho utilizzato personalmente tutte le opzioni presentate qui.


1

Se qualcuno sta ancora leggendo questo o capita di venire qui, questo è il grande vantaggio dell'utilizzo di un sistema di database NoSQL come mongodb. Ho avuto lo stesso problema con la modifica della tabella per aggiungere colonne per funzionalità aggiuntive o indici su una tabella di grandi dimensioni con milioni di righe e scritture elevate. Finirebbe per bloccarsi per molto tempo, quindi farlo sul database LIVE frustrerebbe i nostri utenti. Su tavolini puoi cavartela.

Odio il fatto che dobbiamo "progettare i nostri tavoli per evitare di alterarli". Semplicemente non penso che funzioni nel mondo dei siti web di oggi. Non puoi prevedere come le persone useranno il tuo software, ecco perché cambi rapidamente le cose in base al feedback degli utenti. Con mongodb, puoi aggiungere "colonne" a piacimento senza tempi di inattività. In realtà non li aggiungi nemmeno, inserisci semplicemente i dati con nuove colonne e lo fa automaticamente.

Vale la pena controllare: www.mongodb.com


2
MySQL è ancora utilizzato in molti sistemi, quindi la domanda è davvero su come ottenere una modifica dello schema in SQL RDBMS, anche se sono anche un fervente sostenitore di NoSQL.
Alexy

1

In generale, la risposta sarà "No". Stai cambiando la struttura della tabella che potenzialmente richiederà molti aggiornamenti "e sono assolutamente d'accordo con questo. Se prevedi di farlo spesso, ti offrirò un'alternativa alle colonne" fittizie ": usa VIEWinvece s di tabelle per SELECTl'inserimento di dati. IIRC, la modifica della definizione di una vista è relativamente leggera e l'indirizzamento indiretto attraverso una vista viene eseguito quando il piano di query viene compilato. La spesa è che dovresti aggiungere la colonna a una nuova tabella e fare vista JOINnella colonna.

Ovviamente questo funziona solo se puoi usare chiavi esterne per eseguire la cascata di eliminazioni e quant'altro. L'altro vantaggio è che puoi creare una nuova tabella contenente una combinazione di dati e puntare la vista su di essa senza disturbare l'utilizzo del client.

Solo un pensiero.


1

La differenza tra Postgres e MySQL a questo proposito è che in Postgres non ricrea una tabella, ma modifica il dizionario dei dati che è simile a Oracle. Pertanto, l'operazione è veloce, mentre è ancora necessario allocare un blocco di tabella DDL esclusivo per un tempo molto breve come indicato sopra da altri.

In MySQL l'operazione copierà i dati in una nuova tabella bloccando le transazioni, il che costituiva il problema principale per i DBA MySQL prima della v. 5.6.

La buona notizia è che dal rilascio di MySQL 5.6 la restrizione è stata per lo più rimossa e ora puoi goderti la vera potenza del database MYSQL.


3
Sembra che tu stia cercando di collegarti a un riferimento riguardante una modifica in MySql 5.6, ma non ha funzionato. Per favore riprova.
dg99



0

Le colonne fittizie sono una buona idea se puoi prevederne il tipo (e renderle annullabili). Controlla come il tuo motore di archiviazione gestisce i valori nulli.

MyISAM bloccherà tutto se menzionerai anche di sfuggita il nome di un tavolo, al telefono, in aeroporto. Lo fa solo ...

Detto questo, le serrature non sono davvero un grosso problema; fintanto che non stai cercando di aggiungere un valore predefinito per la nuova colonna a ogni riga, ma lascia che sia nullo e il tuo motore di archiviazione è abbastanza intelligente da non scriverlo, dovresti stare bene con un lucchetto che è solo tenuto abbastanza a lungo per aggiornare i metadati. Se provi a scrivere un nuovo valore, beh, sei brindisi.


1
Ho provato ad aggiungere una colonna NULL a una tabella InnoDB e ha dovuto ricostruire l'intera tabella; non una semplice operazione di "aggiornamento dei metadati".
Daniel,

Penso che l'idea fosse quella di includere colonne extra, nullable, nel database quando è progettato, in modo che se è richiesta una nuova funzionalità si può "aggiungere" una nuova colonna semplicemente iniziando a usarla. Non avrà un bel nome, ma se il tipo di dati è stato scelto / previsto correttamente dovrebbe funzionare.
supercat

0

TokuDB può aggiungere / eliminare colonne e aggiungere indici "caldi", la tabella è completamente disponibile durante tutto il processo. È disponibile tramite www.tokutek.com


-6

Non proprio.

Stai alterando la struttura sottostante della tabella, dopotutto, e questo è un po 'di informazione che è abbastanza importante per il sistema sottostante. Stai anche (probabilmente) spostando gran parte dei dati su disco.

Se hai intenzione di farlo molto, è meglio riempire semplicemente la tabella con colonne "fittizie" disponibili per un uso futuro.


3
Riempire una tabella con colonne fittizie sembra essere una pessima idea.
Jost
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.