Comando DELETE non completato su 30.000.000 di righe


22

Ho ereditato un database e sto cercando di pulirlo e accelerarlo. Ho una tabella che contiene 30.000.000 di righe, molte delle quali sono dati spazzatura inseriti a causa di un errore per conto del nostro programmatore. Prima di aggiungere nuovi indici più ottimizzati, ho convertito la tabella da MyISAM a InnoDB e sto cercando di eliminare molte delle righe che contengono dati spazzatura.

Il database è MySQL 5.0 e ho accesso root al server. Prima eseguivo questi comandi tramite Adminer e poi phpMyAdmin, entrambi con gli stessi risultati.

Il comando che sto eseguendo è,

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-%'

In sostanza, elimina tutto in questa colonna che inizia con un trattino -.

Funziona per circa 3-5 minuti e poi quando visualizzo l'elenco dei processi, non c'è più.

Poi corro,

SELECT *
FROM `tablename`
WHERE `columnname` LIKE '-%'

e restituisce milioni di righe.

Perché la mia dichiarazione di eliminazione non viene completata?

PS, sono consapevole di quanto sia obsoleto MySQL 5.0. Sto lavorando per spostare il DB su MySQL 5.6 w InnoDB (forse MariaDB 10 w XtraDB) ma fino a quando ciò accade, sto cercando di rispondere a questo con il DB così com'è.

-

Modifica rimossa, vedi la mia risposta.

Risposte:


24

Si prega di guardare l'architettura di InnoDB (foto dal CTO di Percona Vadim Tkachenko)

Impianto idraulico InnoDB

Le righe che stai eliminando vengono scritte nei registri di annullamento. Il file ibdata1 dovrebbe crescere in questo momento per la durata dell'eliminazione. Secondo mysqlperformanceblog.comReasons for run-away main Innodb Tablespace :

  • Molte modifiche transazionali
  • Transazioni molto lunghe
  • Filo di spurgo in ritardo

Nel tuo caso, il motivo n. 1 occuperebbe un segmento di rollback insieme a parte dello spazio di annullamento poiché stai eliminando le righe. Le righe devono trovarsi in ibdata1 fino al termine dell'eliminazione. Quello spazio viene logicamente scartato ma lo spazio su disco non si riduce.

Devi eliminare quell'eliminazione in questo momento. Una volta terminata la query di eliminazione, verranno ripristinate le righe eliminate.

Fai invece questo:

CREATE TABLE tablename_new LIKE tablename;
INSERT INTO tablename_new SELECT * FROM tablename WHERE `columnname` NOT LIKE '-%';
RENAME TABLE
    tablename TO tablename_old,
    tablename_new TO tablename
;
DROP TABLE tablename_old;

Avresti potuto farlo prima con la versione MyISAM della tabella. Quindi, convertilo in InnoDB.


21

Penso che potremmo aver complicato troppo la risposta che era richiesta nel mio caso . Non ho dubbi sul fatto che sia Roland che Rick James siano corretti nella creazione di una tabella temporanea, iniettando solo righe che passano il filtro NOT LIKE '-%'ma la soluzione per me è stata "più semplice" perché c'era un errore importante di cui non ero a conoscenza fino ad ora e per che mi scuso.

Ho eseguito la query nel mysqlprompt interattivo e ho notato il messaggio di errore,

mysql> DELETE FROM `slugs` WHERE `slug` LIKE '-%';
ERROR 1206 (HY000): The total number of locks exceeds the lock table size

Attraverso Googleing l'errore, ho scoperto che la soluzione era aumentare innodb_buffer_pool_sizetramite il /etc/my.cnffile e riavviare il demone mysql. Per il mio server era impostato sul valore predefinito 8Me l'ho aumentato a 1G(il server ha 32 GB e questa è l'unica tabella che è attualmente InnoDB).

mysql> DELETE FROM `slugs` WHERE `slug` LIKE '-%';
Query OK, 23517226 rows affected (27 min 33.23 sec)

Quindi sono stato in grado di eseguire il comando ed eliminare 23 milioni di record in ~ 27 minuti.

Per i curiosi su cosa innodb_buffer_pool_sizedovrebbe essere impostato, prendi nota di quanta RAM hai e poi dai un'occhiata a questo thread che fornisce una stima suggerita in GB.


12

Il suggerimento di Roland può essere accelerato facendo entrambe le cose contemporaneamente:

CREATE TABLE tablename_new LIKE tablename;
ALTER TABLE tablename_new ENGINE = InnoDB;
INSERT INTO tablename_new 
    SELECT * FROM tablename WHERE `columnname` NOT LIKE '-%' ORDER BY primary_key;
RENAME TABLE
    tablename TO tablename_old,
    tablename_new TO tablename
;
DROP TABLE tablename_old;

Ma ecco un blog che spiega come fare grandi DELETE a pezzi, piuttosto che apparentemente impiegare per sempre: http://mysql.rjweb.org/doc.php/deletebig L'essenza è di camminare attraverso il tavolo tramite il PK, facendo 1K righe contemporaneamente. (Naturalmente ci sono più dettagli di cui essere a conoscenza.)

E questo blog affronta potenziali gotcha nella conversione in InnoDB: http://mysql.rjweb.org/doc.php/myisam2innodb


5

Il mio primo istinto sarebbe quello di fare più, piccole eliminazioni limitando il numero di risultati della query ed eseguendo la query più volte:

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-%' LIMIT 1000000

Un inconveniente di questo approccio: ogni eliminazione richiederà sempre più tempo. Questo perché deve saltare sempre più righe che non corrispondono a WHERE.
Rick James,

È vero, ma se questo processo non si verifica troppo spesso, le scansioni di tabelle complete multiple non dovrebbero essere così gravi come il problema originale da risolvere, il che significa che la query non viene mai completata a causa della dimensione del registro annullata.
kristianp,

Punto valido. (Vorrei LIMITabbassare; dire 10000.)
Rick James,

4

La soluzione più semplice è semplicemente non farlo: esegui una cancellazione più piccola, che può essere elaborata più facilmente.

In questo caso, avrei raccomandato di provare a eliminare sequenzialmente il modulo:

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-a%'

2

Forse potresti fare qualcosa del genere:

  • Aggiungi un nuovo campo chiamato deleted.
  • Fai un aggiornamento come UPDATE tablename SET deleted=1 WHERE `columnname` LIKE '-a%'.
  • Imposta cronper eliminarlo di notte.

L'aggiornamento può richiedere fino all'eliminazione.
Rick James,
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.