Domanda

Quindi ho questa tabella di controllo (azioni tracce su una 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 l'archiviazione dei prodotti obsoleti. La tabella è cresciuta fino a circa 50 milioni le righe, in modo che il modo più veloce ho potuto eliminare le righe è stato quello di eliminare una tabella alla volta (in base a tableName).

Questo funziona abbastanza bene, ma su alcune delle tabelle che sono write-pesante, non sarà completa. La mia interrogazione cancella tutti gli elementi che hanno un'azione delete associato su 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)
)

I lasciate che questo funzionare sul mio server per 3 giorni e non è mai completato per il tavolo più grande. L'spiegare uscita (se passo l'eliminazione 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 prendere 3 giorni per eliminare, penserei. Ho il mio innodb_buffer_pool_size insieme a 3 GB, e il server non è impostato per utilizzare one_file_per_table. In quali altri modi posso migliorare InnoDB prestazioni di eliminazione? (Running MySQL 5.1.43 su Mac OSX)

È stato utile?

Soluzione

Si potrebbe cancellare i dati in batch.

In SQL Server, la sintassi è righe delete top X da una tabella. È quindi farlo in un ciclo, con una transazione per ogni lotto (se si dispone di più di una dichiarazione, ovviamente), in modo da tenere operazioni a breve e mantenere le serrature solo per brevi periodi.

In MySQL sintassi: DELETE FROM userTable LIMIT 1000

Ci sono restrizioni su che (non è possibile utilizzare LIMIT in eliminazioni con si unisce, per esempio) ma in questo caso si potrebbe essere in grado di farlo in quel modo.

C'è un pericolo aggiuntivo per utilizzare LIMIT con DELETE quando si tratta di replica; le file eliminati non sono a volte cancellati nello stesso ordine sullo slave come è stato eliminato sul master.

Altri suggerimenti

Provare a usare un approccio tabella temporanea. Provare qualcosa di simile:

Passo 1) CREATE TABLE track_table_new LIKE track_table;

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

Passo 3) ALTER TABLE track_table RENAME track_table_old;

Passo 4) ALTER TABLE track_table_new RENAME track_table;

Passo 5) DROP TABLE track_table_old;

I non ha incluso il campo tupla nella Fase 2. Si prega di vedere se questo produce l'effetto desiderato. Se questo è ciò che si vuole, si consiglia di abbandonare il campo tupla del tutto a meno che non si utilizza il campo tupla per altri motivi.

La cancellazione di righe indesiderate in lotti dovrebbe tenere altra operazione praticabile. Ma la vostra eliminazione di operazione ha condizioni, in modo da garantire che vi sia un indice appropriato su palafitte poste sulle condizioni.

A causa di 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 utilizzare questo indice per la scansione dei file che sono anteriori alla vostra condizione datetime.

Con tale indice, si può 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
| 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 |

-Fist, dalle vostre spiegare i key_len così grande => è necessario declassare la dimensione più piccola possibile. Per la query Penso che il modo migliore è il tipo di modifica dei dati di campo di azione da char (12) per tinyint, quindi l'aspetto di mappatura dei dati come:

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

ed è possibile modificare table_id invece nometabella troppo. il DDL per la migliore interpretazione possibile:

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;

in modo che il query può correre apparire come:

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 è stato l'utilizzo di partizioni. in modo da poter eliminare partizione. Attualmente, il mio tavolo hanno ottenuto qualcosa di più righe 40mil. e aggiornare oraria (aggiornamento 400k righe per ogni volta), e posso rilasciare i dati della partizione e ricarica curr_date nella tabella. il comando DROP molto veloce (<100 ms). Spero che questo aiuto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a dba.stackexchange
scroll top