Come migliorare le prestazioni di ELIMINAZIONE di InnoDB?


9

Quindi ho questa tabella di controllo (tiene traccia delle azioni su qualsiasi tabella nel mio database):

CREATE TABLE `track_table` (
  `id` int(16) unsigned NOT NULL,
  `userID` smallint(16) unsigned NOT NULL,
  `tableName` varchar(255) NOT NULL DEFAULT '',
  `tupleID` int(16) unsigned NOT NULL,
  `date_insert` datetime NOT NULL,
  `action` char(12) NOT NULL DEFAULT '',
  `className` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `userID` (`userID`),
  KEY `tableID` (`tableName`,`tupleID`,`date_insert`),
  KEY `actionDate` (`action`,`date_insert`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

e ho bisogno di iniziare ad archiviare oggetti obsoleti. La tabella è cresciuta fino a circa 50 milioni di righe, quindi il modo più veloce che ho potuto eliminare le righe era di eliminarlo una tabella alla volta (basato su tableName).

Funziona abbastanza bene ma su alcuni dei tavoli che sono pesanti da scrivere, non si completa. La mia query elimina tutti gli elementi a cui è associata deleteun'azione in una combinazione tupleID / tableName:

DELETE FROM track_table WHERE tableName='someTable' AND tupleID IN (
  SELECT DISTINCT tupleID FROM track_table
  WHERE tableName='someTable' AND action='DELETE' AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
)

Ho lasciato correre questo sul mio server per 3 giorni e non è mai stato completato per la tabella più grande. L'output di spiegazione (se cambio la cancellazione per selezionare:

| id | select_type        | table       | type | possible_keys      | key     | key_len | ref        | rows    | Extra                        |
|  1 | PRIMARY            | track_table | ref  | tableID            | tableID | 257     | const      | 3941832 | Using where                  |
|  2 | DEPENDENT SUBQUERY | track_table | ref  | tableID,actionDate | tableID | 261     | const,func |       1 | Using where; Using temporary |

Quindi 4 milioni di righe non dovrebbero richiedere 3 giorni per essere eliminate, penso. Ho innodb_buffer_pool_size impostato su 3 GB e il server non è impostato per utilizzare one_file_per_table. In quali altri modi posso migliorare le prestazioni di eliminazione di InnoDB? (Esecuzione di MySQL 5.1.43 su Mac OSX)

Risposte:


11

È possibile eliminare i dati in batch.

In SQL Server, la sintassi è delete top Xrighe da una tabella. Lo fai quindi in un ciclo, con una transazione per ogni batch (se hai più di una dichiarazione, ovviamente), in modo da mantenere brevi le transazioni e mantenere i blocchi solo per brevi periodi.

Nella sintassi di MySQL: DELETE FROM userTable LIMIT 1000

Esistono delle restrizioni (ad esempio, non è possibile utilizzarle LIMITnelle eliminazioni con join), ma in questo caso potresti essere in grado di farlo in questo modo.

Esiste un ulteriore pericolo nell'uso LIMITdi DELETEquando si tratta di replica; le righe eliminate a volte non vengono eliminate nello stesso ordine sullo slave come è stato eliminato sul master.


6

Prova a utilizzare un approccio di tabella temporanea. Prova qualcosa del genere:

Passo 1) CREATE TABLE track_table_new LIKE track_table;

Passo 2) INSERT INTO track_table_new SELECT * FROM track_table WHERE action='DELETE' AND date_insert >= DATE_SUB(CURDATE(), INTERVAL 30 day);

Passaggio 3) ALTER TABLE track_table RENAME track_table_old;

Passaggio 4) ALTER TABLE track_table_new RENAME track_table;

Passaggio 5) DROP TABLE track_table_old;

Non ho incluso il campo tupla nel passaggio 2. Vedere se questo produce l'effetto desiderato. Se questo è ciò che desideri, potresti voler abbandonare del tutto il campo tupla a meno che tu non usi il campo tupla per altri motivi.


Questa è una soluzione interessante. Ho bisogno del campo tupla nella tabella. tableName / tupleID è una chiave esterna non definita della tabella che viene registrata. Non definito perché fino a poco tempo fa questa tabella era MyISAM, che non supporta le chiavi esterne.
Derek Downey,

1

La cancellazione di righe indesiderate in batch dovrebbe consentire di eseguire altre operazioni. Ma la tua cancellazione dell'operazione ha condizioni, quindi assicurati che ci sia un indice appropriato sulle colonne rispetto alle condizioni.

Perché MySQL non supporta la funzione completa di indice di scansione sciolto, si può tentare di regolare la sequenza per KEY actionDate (action, date_insert)a KEY actionDate (date_insert, action). Con il prefisso "date_insert", MySQL dovrebbe usare questo indice per scansionare le righe che sono precedenti alla tua condizione datetime.

Con tale indice, è possibile scrivere SQL come:

DELETE
FROM track_table
WHERE tableName='someTable'
    AND action='DELETE'
    AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
LIMIT 1000 -- Your size of batch

1
| id | select_type        | table       | type | possible_keys      | key     | key_len | ref        | rows    | Extra                        |
|  1 | PRIMARY            | track_table | ref  | tableID            | tableID | 257     | const      | 3941832 | Using where                  |
|  2 | DEPENDENT SUBQUERY | track_table | ref  | tableID,actionDate | tableID | 261     | const,func |       1 | Using where; Using temporary |

-Pugno, dalla tua spiegazione key_len così grande => devi ridurre le dimensioni il più piccolo possibile. Per la tua domanda, penso che il modo migliore sia cambiare il tipo di dati del campo di azione da char (12) a tinyint, quindi la mappatura dei dati assomiglia a:

1: -> DELETE
2: -> UPDATE
3: -> INSERT
...

e puoi anche cambiare table_id invece tablename. il DDL per le migliori prestazioni può:

CREATE TABLE `track_table` (
  `id` int(11) unsigned NOT NULL,
  `userID` smallint(6) unsigned NOT NULL,
  `tableid` smallint(6) UNSIGNED NOT NULL DEFAULT 0,
  `tupleID` int(11) unsigned NOT NULL,
  `date_insert` datetime NOT NULL,
  `actionid` tinyin(4) UNSIGNED NOT NULL DEFAULT 0,
  `className` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `userID` (`userID`),
  KEY `tableID` (`tableid`,`tupleID`,`date_insert`),
  KEY `actionDate` (`actionid`,`date_insert`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `actions` (
  `id` tinyint(4) unsigned NOT NULL 
  `actionname` varchar(255) NOT NULL,
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `table_name` (
  `id` tinyint(4) unsigned NOT NULL 
  `tablename` varchar(255) NOT NULL,
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

quindi la query può essere simile a:

DELETE FROM track_table WHERE tableid=@tblid AND tupleID IN (
  SELECT DISTINCT tupleID FROM track_table
  WHERE tableid=@tblid AND actionid=@actionid AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
).

Ma il modo più veloce era usare la partizione. così puoi eliminare la partizione. Attualmente, il mio tavolo ha circa 40 milioni di righe. e aggiorno ogni ora (400k righe di aggiornamento per ogni volta), e posso rilasciare la partizione curr_date e ricaricare i dati nella tabella. il comando drop molto veloce (<100ms). Spero che questo aiuto.

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.