Una storia di cancellazione fisica di 300 milioni di record in MySQL

Introduzione

Ciao. Sono ningenMe, sviluppatore web.

Come dice il titolo, la mia storia è quella di una rimozione fisica di 300 milioni di record in MySQL.

Sono stato interessato a questo, quindi ho deciso di preparare un promemoria (istruzioni).

Inizio - Alert

Nel batch server, che utilizzo e gestisco, c'è un processo regolare che una volta al giorno raccoglie i dati dell'ultimo mese da MySQL.

Di solito, questo processo termina dopo circa 1 ora, ma questa volta non si è concluso per 7 o 8 ore, e l'alert continuava a comparire...

Ricerca della causa

Ho provato a riavviare il processo, a controllare i log, ma non ho visto nulla di preoccupante.
La query era indicizzata correttamente. Ma quando ho riflettuto su cosa stesse andando storto, ho capito che le dimensioni del database erano piuttosto grandi.

hoge_table | 350'000'000 |

350 milioni di record. Sembra che l'indicizzazione funzionasse correttamente, solo molto lentamente.

La raccolta di dati richiesta per il mese ammontava a circa 12.000.000 di record. Sembra che il comando select abbia impiegato molto tempo e la transazione non sia stata eseguita per lungo tempo.

DB

Fondamentalmente, è una tabella che cresce ogni giorno di circa 400.000 record. Il database avrebbe dovuto raccogliere dati solo per l'ultimo mese, quindi il calcolo era fatto affinché sopportasse proprio quel volume di dati, ma sfortunatamente, l'operazione di rotation non era attivata.

Questo database non è stato progettato da me. L'ho ricevuto da un altro sviluppatore, quindi c'è la sensazione che si tratti di un debito tecnico.

È arrivato il momento in cui il volume dei dati inseriti quotidianamente è diventato elevato e ha finalmente raggiunto un limite. Si presume che, lavorando con un volume così grande di dati, sia necessario suddividerli, ma purtroppo questo non è stato fatto.

E qui sono intervenuto io.

Correzione

Era più razionale ridurre il database stesso e ridurre il tempo necessario per elaborarlo piuttosto che cambiare la logica stessa.

La situazione dovrebbe cambiare notevolmente se si eliminano 300 milioni di record, quindi ho deciso di farlo... Ah, pensavo che avrebbe funzionato di certo.

Azione 1

Dopo aver preparato un backup affidabile, ho finalmente iniziato a inviare le query.

「Invio della query」

DELETE FROM hoge_table WHERE create_time <= 'YYYY-MM-DD HH:MM:SS';

「…」

「…」

“Hmm... Nessuna risposta. Magari il processo sta impiegando molto tempo?” — pensai, ma per scrupolo guardai in grafana e vidi che il carico del disco stava aumentando molto rapidamente.
«Pericoloso» — pensai di nuovo e subito fermai la query.

Azione 2

Analizzando tutto, ho capito che il volume dei dati era troppo grande per eliminarli tutti in una sola volta.

Ho deciso di scrivere uno script che potesse eliminare circa 1.000.000 di record e l'ho avviato.

「realizzerò lo script」

“Adesso funzionerà sicuramente,” pensai.

Azione 3

Il secondo metodo ha funzionato, ma si è rivelato molto laborioso.
Per fare tutto in modo ordinato, senza nervosismi, ci sarebbero volute circa due settimane. Tuttavia, questo scenario non soddisfaceva i requisiti di servizio, quindi ho dovuto abbandonarlo.

Perciò, ecco cosa ho deciso di fare:

Copiare la tabella e rinominarla

Dallo step precedente ho capito che eliminare un volume così grande di dati crea un carico altrettanto elevato. Quindi ho deciso di creare una nuova tabella da zero usando insert e trasferirci i dati che intendevo eliminare.

| hoge_table     | 350'000'000|
| tmp_hoge_table |  50'000'000|

Se creo una nuova tabella delle stesse dimensioni di quella sopra, la velocità di elaborazione dei dati dovrebbe aumentare di 1/7.

Creata la tabella e rinominata, ho cominciato a usarla come tabella master. Ora, se elimino la tabella con 300 milioni di record, dovrebbe andare tutto bene.
Ho appreso che truncate o drop creano un carico minore rispetto a delete e ho deciso di utilizzare questo metodo.

Esecuzione

「Invio della query」

INSERT INTO tmp_hoge_table SELECT FROM hoge_table create_time > 'YYYY-MM-DD HH:MM:SS';

「…」
「…」
「eh...?」

Azione 4

Pensavo che l'idea precedente avrebbe funzionato, ma dopo aver inviato la richiesta di insert è comparso un errore multiplo. MySQL non guarda in faccia a nessuno.

Ero così stanco che ho iniziato a pensare che non avrei più voluto occuparmene.

Ho riflettuto un po' e ho capito che, forse, per una sola volta erano troppe le richieste di insert…
Ho provato a inviare una richiesta di insert per il volume di dati che il database deve elaborare in un giorno. Ci sono riuscito!

E dopo questo continuiamo ad inviare richieste per lo stesso volume di dati. Poiché dobbiamo rimuovere il volume mensile di dati, ripetiamo questa operazione circa 35 volte.

Rinominare la tabella

Qui la fortuna era dalla mia parte: tutto è andato liscio.

Gli Alert sono scomparsi

La velocità di elaborazione in batch è aumentata.

In precedenza, questo processo durava circa un'ora, ora ci vogliono circa 2 minuti.

Dopo essermi assicurato che tutti i problemi fossero risolti, ho eliminato 300 milioni di record. Ho rimosso la tabella e mi sono sentito rinato.

Riassunto

Ho capito che nel processo batch è stata trascurata la rotazione e questo era il problema principale. Un tale errore nell'architettura porta a una perdita di tempo.

Ti sei mai chiesto il carico durante la replica dei dati, rimuovendo record dal database? Non sovraccarichiamo MySQL.

Coloro che comprendono bene i database non si troveranno certamente ad affrontare tale problema. Spero che questo articolo sia stato utile agli altri.

Grazie per aver letto!

Saremmo molto felici se ci dicessi se ti è piaciuto questo articolo, se la traduzione è stata chiara e se ti è stata utile.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster