Crea un indice su un'enorme tabella di produzione MySQL senza blocco della tabella


104

Devo creare un indice su una tabella MySQL di circa 5 milioni di righe. È una tabella di produzione e temo un blocco completo di tutto se eseguo un'istruzione CREATE INDEX ...

C'è un modo per creare quell'indice senza bloccare inserimenti e selezioni?

Mi chiedevo solo di non fermarmi, creare un indice e riavviare il mio sistema!


1
assicurati che myisam_sort_buffer_size e myisam_max_sort_file_size siano abbastanza grandi.
Jon Black

Risposte:


130

[2017] Aggiornamento: MySQL 5.6 supporta gli aggiornamenti dell'indice online

https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html#online-ddl-index-syntax-notes

In MySQL 5.6 e versioni successive, la tabella rimane disponibile per le operazioni di lettura e scrittura durante la creazione o l'eliminazione dell'indice. L'istruzione CREATE INDEX o DROP INDEX termina solo dopo che tutte le transazioni che accedono alla tabella sono state completate, in modo che lo stato iniziale dell'indice rifletta il contenuto più recente della tabella. In precedenza, la modifica della tabella durante la creazione o l'eliminazione di un indice in genere causava un deadlock che annullava l'istruzione INSERT, UPDATE o DELETE sulla tabella.

[2015] L'aggiornamento della tabella indica che blocca le scritture in MySQL 5.5

Dalla risposta sopra:

"Se utilizzi una versione superiore alla 5.1, gli indici vengono creati mentre il database è online. Quindi non preoccuparti, non interromperai l'utilizzo del sistema di produzione."

Questo è **** FALSO **** (almeno per le tabelle MyISAM / InnoDB, che è ciò che usa il 99,999% delle persone là fuori. L'edizione cluster è diversa.)

L'esecuzione di operazioni di AGGIORNAMENTO su una tabella si BLOCCA durante la creazione dell'indice. MySQL è davvero, davvero stupido su questo (e su poche altre cose).

Script di test:

(   
  for n in {1..50}; do
    #(time mysql -uroot -e 'select  * from website_development.users where id = 41225\G'>/dev/null) 2>&1 | grep real;
    (time mysql -uroot -e 'update website_development.users set bio="" where id = 41225\G'>/dev/null) 2>&1 | grep real;
  done
) | cat -n &
PID=$!
sleep 0.05
echo "Index Update - START"
mysql -uroot website_development -e 'alter table users add index ddopsonfu (last_name, email, first_name, confirmation_token, current_sign_in_ip);'
echo "Index Update - FINISH"
sleep 0.05
kill $PID
time mysql -uroot website_development -e 'drop index ddopsonfu on users;'

Il mio server (InnoDB):

Server version: 5.5.25a Source distribution

Output (notare come la sesta operazione si blocca per i ~ 400 ms necessari per completare l'aggiornamento dell'indice):

 1  real    0m0.009s
 2  real    0m0.009s
 3  real    0m0.009s
 4  real    0m0.012s
 5  real    0m0.009s
Index Update - START
Index Update - FINISH
 6  real    0m0.388s
 7  real    0m0.009s
 8  real    0m0.009s
 9  real    0m0.009s
10  real    0m0.009s
11  real    0m0.009s

Vs operazioni di lettura che non bloccano (scambia il commento di riga nello script):

 1  real    0m0.010s
 2  real    0m0.009s
 3  real    0m0.009s
 4  real    0m0.010s
 5  real    0m0.009s
Index Update - START
 6  real    0m0.010s
 7  real    0m0.010s
 8  real    0m0.011s
 9  real    0m0.010s
...
41  real    0m0.009s
42  real    0m0.010s
43  real    0m0.009s
Index Update - FINISH
44  real    0m0.012s
45  real    0m0.009s
46  real    0m0.009s
47  real    0m0.010s
48  real    0m0.009s

Aggiornamento dello schema di MySQL senza tempi di inattività

Finora, c'è solo un metodo che conosco per aggiornare uno schema MySql e non subire un'interruzione della disponibilità. Maestri circolari:

  • Il Master A ha il tuo database MySQL in esecuzione su di esso
  • Metti in servizio il Master B e fallo replicare le scritture dal Master A (B è uno schiavo di A)
  • Eseguire l'aggiornamento dello schema su Master B. Rimarrà indietro durante l'aggiornamento
  • Lascia che il Maestro B raggiunga. Invariant: la modifica dello schema DEVE essere in grado di elaborare comandi replicati da uno schema downversion. Le modifiche all'indicizzazione si qualificano. Solitamente si qualificano semplici aggiunte di colonne. Rimuovere una colonna? probabilmente no.
  • Scambia ATOMICAMENTE tutti i client dal Master A al Master B. Se vuoi essere al sicuro (credimi, lo fai), dovresti assicurarti che l'ultima scrittura su A sia replicata su B PRIMAB esegue la prima scrittura. Se consenti le scritture simultanee su 2+ master, ... comprendi meglio la replica di MySQL a un livello PROFONDO o sei destinato a un mondo di dolore. Dolore estremo. Ad esempio, hai una colonna AUTOINCREMENT ??? sei fregato (a meno che non usi i numeri pari su un master e le probabilità sull'altro). NON fidarti della replica di MySQL per "fare la cosa giusta". NON è intelligente e non ti salverà. È solo leggermente meno sicuro che copiare i registri delle transazioni binarie dalla riga di comando e riprodurli a mano. Tuttavia, disconnettere tutti i client dal vecchio master e spostarli nel nuovo master può essere eseguito in pochi secondi, molto più velocemente dell'attesa per un aggiornamento dello schema di più ore.
  • Ora il Maestro B è il tuo nuovo maestro. Hai il nuovo schema. La vita è bella. Prendere una birra; il peggio è passato.
  • Ripeti il ​​processo con il Master A, aggiornando il suo schema in modo che diventi il ​​tuo nuovo master secondario, pronto a subentrare nel caso in cui il tuo master primario (master B ora) perda potenza o semplicemente su e muore su di te.

Un modo semplice per aggiornare lo schema non lo è. Realizzabile in un ambiente di produzione serio; sì. Per favore, per favore, per favore, se c'è un modo più semplice per aggiungere un indice a una tabella MySQL senza bloccare le scritture, fammelo sapere.

Google mi ha portato a questo articolo che descrive una tecnica simile. Ancora meglio, consigliano di bere nello stesso punto della procedura (Nota che ho scritto la mia risposta prima di leggere l'articolo)!

La modifica dello schema online di Percona

L' articolo che ho collegato sopra parla di uno strumento, pt-online-schema-change , che funziona come segue:

  • Crea una nuova tabella con la stessa struttura dell'originale.
  • Aggiorna lo schema sulla nuova tabella.
  • Aggiungi un trigger alla tabella originale in modo che le modifiche siano mantenute sincronizzate con la copia
  • Copia le righe in batch dalla tabella originale.
  • Spostare la tabella originale fuori mano e sostituirla con una nuova tabella.
  • Lascia cadere il vecchio tavolo.

Non ho mai provato lo strumento da solo. YMMV

RDS

Attualmente sto utilizzando MySQL tramite RDS di Amazon . È un servizio davvero ingegnoso che avvolge e gestisce MySQL, permettendoti di aggiungere nuove repliche di lettura con un solo pulsante e aggiornare in modo trasparente il database attraverso gli SKU hardware. È davvero conveniente. Non hai un accesso SUPER al database, quindi non puoi fottere direttamente la replica (è una benedizione o una maledizione?). Tuttavia, è possibile utilizzare la promozione della replica in lettura per apportare modifiche allo schema su uno slave di sola lettura, quindi promuovere tale slave affinché diventi il ​​nuovo master. Esattamente lo stesso trucco che ho descritto sopra, solo molto più facile da eseguire. Non fanno ancora molto per aiutarti con il cut-over. Devi riconfigurare e riavviare la tua app.


3
pt-online-schema-change funziona alla grande anche in una replica master-slave. L'ho usato per eseguire la migrazione in tempo reale su una tabella di record di 20 milioni di letture occupate sul nostro database master di produzione con 2 slave di replica senza intoppi o tempi di inattività. Ci vuole un po 'di tempo per preparare lo script e di solito devo creare un file .sql contenente la modifica SQL non elaborata e un file .sh come wrapper per eseguire lo stesso SQL ma in formato frammento (no ALTER TABLE). Puoi eseguire più comandi con pt-online-schema-change stringendoli e separati da virgole.
Alex Le

-1; Non conosco le versioni precedenti, ma so che la creazione dell'indice non blocca il DML simultaneo in MySQL 5.6+ (per il quale esisteva un RC al momento in cui è stata scritta questa risposta e che era stato ufficialmente rilasciato quando questa risposta è durata modificato a maggio 2013) perché mi sono affidato a questo per eseguire creazioni di indici di più ore su tabelle di produzione pur accettando inserimenti. E mentre si può essere di destra circa la creazione dell'indice blocco DML in 5.5 e di seguito, il ritardo inferiore al secondo dimostrato qui non è del tutto convincente.
Mark Amery

@MarkAmery: il comportamento di blocco è un comportamento di blocco e 400 ms è un'eternità. Blocchi MySQL 5.5 per gli aggiornamenti dell'indice. Crea un database di test più grande e si bloccherà per secondi, ore o giorni. Ho scritto questo post prima che MySQL 5.6 avesse aggiornamenti dello schema online, quindi il mio contenuto originale non riflette questo fatto. Ho aggiornato il post per riflettere le nuove informazioni disponibili.
Dave Dopson

@DaveDopson, sei sicuro al 100% che solo le operazioni di UPDATE siano bloccate?
toto_tico

Questo è stato il caso della versione che ho provato.
Dave Dopson

67

Come sottolinea questo post del blog , il ALTER TABLEmeccanismo InnoDB è stato completamente riprogettato per MySQL 5.6.

(Per una panoramica esclusiva di questo argomento, la documentazione di MySQL può fornire un pomeriggio di lettura.)

Per aggiungere un indice a una tabella senza che venga generato un blocco su UPDATE/ INSERT, è possibile utilizzare il seguente formato di istruzione:

ALTER TABLE my_table ADD INDEX my_table__idx (my_column), ALGORITHM=INPLACE, LOCK=NONE;


16

Aggiornamento MySQL 5.6 (febbraio 2013): ora puoi eseguire operazioni di lettura e scrittura mentre viene creato un indice anche con tabelle InnoDB - http://dev.mysql.com/doc/refman/5.6/en/innodb-create-index -overview.html

In MySQL 5.6 e versioni successive, la tabella rimane disponibile per le operazioni di lettura e scrittura durante la creazione o l'eliminazione dell'indice. L'istruzione CREATE INDEX o DROP INDEX termina solo dopo che tutte le transazioni che accedono alla tabella sono state completate, in modo che lo stato iniziale dell'indice rifletta il contenuto più recente della tabella. In precedenza, la modifica della tabella durante la creazione o l'eliminazione di un indice in genere causava un deadlock che annullava l'istruzione INSERT, UPDATE o DELETE sulla tabella.

e:

In MySQL 5.6, questa funzionalità diventa più generale: è possibile leggere e scrivere nelle tabelle mentre viene creato un indice e molti altri tipi di operazioni ALTER TABLE possono essere eseguiti senza copiare la tabella, senza bloccare le operazioni DML o entrambi. Pertanto, in MySQL 5.6 e versioni successive, in genere ci riferiamo a questo set di funzionalità come DDL online piuttosto che Creazione rapida dell'indice.

da http://dev.mysql.com/doc/refman/5.6/en/glossary.html#glos_fast_index_creation


Allora come si spiega l'analisi di Dave?
Nikhil Sahu

1
@NikhilSahu Dave chiaramente non stava testando su MySQL 5.6, ma su qualche versione precedente. Si noti che 5.6 non era ancora stato rilasciato nel momento in cui Dave ha pubblicato la revisione iniziale della sua risposta.
Mark Amery

+1. La mia analisi era su MySQL 5.5 (l'ultima disponibile nel 2013). Sto aggiornando la mia risposta per riflettere le nuove funzionalità di MySQL 5.6.
Dave Dopson

3

pt-online-schema-change è la strada da percorrere se vuoi davvero assicurarti che la migrazione non interrompa il sito.

Come ho scritto nel commento sopra, ho diverse esperienze con la modifica dello schema pt-online in produzione. Abbiamo la nostra tabella principale di oltre 20 milioni di record e un master -> 2 slave di replica di sola lettura. Ho eseguito almeno una dozzina di migrazioni con pt-online-schema-change dall'aggiunta di una nuova colonna, alla modifica del set di caratteri, all'aggiunta di diversi indici. Serviamo tonnellate di traffico anche durante il periodo di migrazione e non abbiamo avuto alcun problema. Ovviamente dovresti testare tutti gli script in modo molto approfondito prima di eseguire la produzione.

Ho provato a raggruppare le modifiche in 1 script in modo che pt-online-schema-change debba copiare i dati solo una volta. E fai molta attenzione quando cambi il nome della colonna poiché perderai i tuoi dati. Tuttavia, l'aggiunta di un indice dovrebbe andare bene.


Non sono d'accordo con la tua raccomandazione incondizionata di pt-online-schema-change. È fantastico, ma è eccessivo per molte situazioni in cui le capacità DDL online di MySQL 5.6+ funzionano già bene. Ha anche dei limiti (come non giocare bene con i trigger) e raddoppia la quantità di scrittura necessaria per l'inserimento nella tabella originale mentre è in corso una modifica dello schema. Impiegherà il tuo disco molto più di quanto farebbe una normale modifica dello schema in linea, e quindi ha il potenziale per "far cadere il tuo sito" in circostanze in cui la semplice esecuzione della modifica dello schema avrebbe funzionato bene.
Mark Amery

Ho scritto in base alla mia esperienza effettiva con la modifica dello schema pt-online in quel momento, quindi non sono sicuro del motivo per cui definiresti la mia raccomandazione "non qualificata". Stavamo avendo almeno 1000+ visitatori sul sito in un dato momento quando ho eseguito le modifiche allo schema e, naturalmente, l'IO del disco era faticoso, ma il nostro sito non è andato giù. Anche avere una buona memorizzazione nella cache ha aiutato. Non ho usato MySQL 5.6+ online DDL ma dalla mia esperienza, pt-online-schema-change ha fatto bene il suo lavoro nel nostro caso.
Alex Le

1
@AlexYe Yikes, intendevo "non qualificato" nel senso di "senza riserve" piuttosto che nel senso di "consegnato da qualcuno che non è qualificato per commentare" - quest'ultima interpretazione non mi è venuta in mente finché non ho visto il tuo commento e certamente non lo è è quello che intendevo! cioè stavo dicendo che sebbene pt-online-schema-changesia uno strumento utile, ci sono moltissime situazioni in cui il normale DDL in linea è altrettanto buono e una manciata dove è migliore, quindi ogni sua raccomandazione dovrebbe essere cauta con attenzione piuttosto che universale.
Mark Amery
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.