2011-09-09 14 views
6

Questa query mysql è in esecuzione per circa 10 ore e non è terminata. Qualcosa è terribilmente sbagliato.Eliminazione molto lenta su base mysql con sottoquery

Due tabelle (testo e spam) sono qui. Lo spam memorizza gli ID di spam entreti nel testo che voglio eliminare.

DELETE FROM tname.text WHERE old_id IN (SELECT textid FROM spam); 

lo spam ha solo 2 colonne, entrambi sono ints. Le voci 800K hanno una dimensione del file di diversi Mbs. Entrambe le int sono chiavi primarie.

testo ha 3 colonne. id (prim key), testo, flag. circa 1200K voci e circa 2,1 gigabyte (più spam).

Il server è un xeon quad, ram da 2 gigabyte (non chiedetemi perché). Solo apache (perché?) E mysqld è in esecuzione. Il suo un vecchio Free BSD e MySQL 4.1.2 (non chiedetemi perché)

Discussioni: 6 Domande: 188805 query lente: 318 si apre: 810 tavoli Flush: 1 tavoli aperti: 157 query al secondo giudizio medio: 7,532

Mysql my.cnf:

[mysqld] 
datadir=/usr/local/mysql 
log-error=/usr/local/mysql/mysqld.err 
pid-file=/usr/local/mysql/mysqld.pid 
tmpdir=/var/tmp 
innodb_data_home_dir = 
innodb_log_files_in_group = 2 
join_buffer_size=2M 
key_buffer_size=32M 
max_allowed_packet=1M 
max_connections=800 
myisam_sort_buffer_size=32M 
query_cache_size=8M 
read_buffer_size=2M 
sort_buffer_size=2M 
table_cache=256 
skip-bdb 
log-slow-queries = slow.log 
long_query_time = 1 

#skip-innodb 
#default-table-type=innodb 
innodb_data_file_path = /usr/local/mysql/ibdata1:10M:autoextend 
innodb_log_group_home_dir = /usr/local/mysql/ 
innodb_buffer_pool_size = 128M 
innodb_log_file_size = 16M 
innodb_log_buffer_size = 8M 
#innodb_flush_log_at_trx_commit=1 
#innodb_additional_mem_pool_size=1M 
#innodb_lock_wait_timeout=50 

log-bin 
server-id=201 

[isamchk] 
key_buffer_size=128M 
read_buffer_size=128M 
write_buffer_size=128M 
sort_buffer_size=128M 

[myisamchk] 
key_buffer_size=128M[server:~] dmesg | grep memory 
real memory = 2146828288 (2047 MB) 
avail memory = 2095534080 (1998 MB) 

read_buffer_size=128M 
write_buffer_size=128M 
sort_buffer_size=128M 
tmpdir=/var/tmp 

La query utilizza una sola CPU, top dice il tempo di CPU del 25% (quindi 1 di 4).

real memory = 2146828288 (2047 MB) 
avail memory = 2095534080 (1998 MB) 

62 processes: 2 running, 60 sleeping 
CPU states: 25.2% user, 0.0% nice, 1.6% system, 0.0% interrupt, 73.2% idle 
Mem: 244M Active, 1430M Inact, 221M Wired, 75M Cache, 112M Buf, 31M Free 
Swap: 4096M Total, 1996K Used, 4094M Free 

    PID USERNAME  THR PRI NICE SIZE RES STATE C TIME WCPU COMMAND 
11536 mysql   27 20 0 239M 224M kserel 3 441:16 94.29% mysqld 

Qualche idea su come risolverlo?

+0

quali sono i motori di archiviazione sui tavoli? – JamesHalsall

+0

La tua query include una colonna old_id, ma la tua descrizione della tabella 'text' no - hai davvero descritto l'intera tabella? Nel complesso, ho il sospetto che questo problema sarebbe magicamente andato via con una versione più recente di MySQL. –

+1

Assicurati di avere indici su 'text.old_id' e' spam.textid'. – Johan

risposta

11

Nella mia esperienza, le sottorequenze sono spesso causa di tempi di esecuzione lenti nelle istruzioni SQL, quindi cerco di evitarli. Prova questo:

DELETE tname FROM tname INNER JOIN spam ON (tname.old_id = spam.textid); 

Declinazione di responsabilità: questa query non è testata, effettuare prima i backup! :-)

+0

-1 per usare la sintassi SQL implicita, uscire dal 1989 e usare invece join espliciti. Anche questo non risolverà il problema perché la tua affermazione non è vera. L'OP deve inserire indici nei campi coinvolti nel join. – Johan

+0

La sua affermazione è molto vera per le versioni MySQL di quella annata. Quando hanno introdotto per la prima volta le subquery e per qualche tempo dopo, ci sono stati molti problemi di prestazioni con loro. –

+0

+1, assicurati anche di avere un indice su spam.textid. – nobody

1

Copia righe che non sono in spam modulo text nella nuova tabella. Quindi eliminare la tabella text e rinominare la tabella creata. La buona idea è di non aggiungere alcuna chiave alla tabella creata. Aggiungi le chiavi dopo averlo rinominato.

+0

Seriamente ??? ..... – Antoniossss

+0

Sì sul serio! Perché non ci penso, la migliore soluzione qui in gran parte delle applicazioni pratiche! – taur

5

La scelta di where id in (select ...) avrà sempre scarse prestazioni.

Al contrario, utilizzare un normale unirsi che sarà molto efficace: selezione

DELETE `text` 
FROM spam 
join `text` on `text`.old_id = spam.textid; 

Avviso dallo spam prima, poi unirsi al testo, che darà le migliori prestazioni.

0

di Corse ci vorrà un sacco di tempo perché eseguire il subquery per ogni record ma utilizzando inner join direttamente questa query viene eseguito solo una volta lascia pensare che la query avrà

10 ms for 50000 rec full time = 50000 * 10 ms ---> 8.333 minutes !! at least don't forget the condition and deleting time ..... 

ma utilizzando join la query verrà eseguita una sola volta:

DELETE t FROM tname.text t INNER JOIN (SELECT textid FROM spam) sq on t.old_id = sq.textid ;