2011-09-09 14 views
6

To zapytanie mysql działa przez około 10 godzin i nie zostało zakończone. Coś jest okropnie nie tak.Bardzo wolne usuwanie na bazie mysql z podzapytaniem

Dwie tabele (tekst i spam) są tutaj. Spam przechowuje numery identyfikacyjne wpisów spamu w tekście, który chcę usunąć.

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

spam ma tylko 2 kolumny, obie to ints. Wpisy 800K mają rozmiar kilku Mbs. Oba serwery są kluczami podstawowymi.

tekst ma 3 kolumny. id (prim key), tekst, flagi. około 1200 000 wpisów i około 2,1 gigabajta (najwięcej spamu).

Serwer to Xeon Quad, 2 GB RAM (nie pytaj mnie dlaczego). Działa tylko apache (why?) I mysqld. Jest to stary darmowy bsd i mysql 4.1.2 (nie pytaj mnie dlaczego)

Liczba wątków: 6 Pytania: 188805 Powolne zapytania: 318 Otwiera: 810 Stoły do ​​spłukiwania: 1 Stoły otwarte: 157 Zapytań na sekundę średnio: 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 

zapytanie jest za pomocą tylko jednego procesora, górna mówi 25% czasu procesora (czyli 1 do 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 

Każdy pomysł, jak to naprawić?

+0

jakie są silniki pamięci na stołach? – JamesHalsall

+0

Twoje zapytanie zawiera kolumnę old_id, ale Twój opis tabeli "text" nie ma - czy Twój naprawdę opisał całą tabelę? Podsumowując, podejrzewam, że problem ten w magiczny sposób zniknie z nowszą wersją MySQL. –

+1

Upewnij się, że masz indeksy na 'text.old_id' i' spam.textid'. – Johan

Odpowiedz

11

Z mojego doświadczenia wynika, że ​​sub zapytania często powodują powolne czasy wykonania w instrukcjach SQL, dlatego staram się ich unikać. Wypróbuj to:

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

Zastrzeżenie: To zapytanie nie jest testowane, najpierw wykonaj kopie zapasowe! :-)

+0

-1 dla używania niejawnej składni SQL, wydostań się z 1989 i zamiast tego użyj jawnych połączeń. To również nie rozwiąże problemu, ponieważ twoje stwierdzenie jest nieprawdziwe. PO musi umieścić indeksy na polach biorących udział w połączeniu. – Johan

+0

Jego twierdzenie jest bardzo prawdziwe w przypadku wersji MySQL tego rocznika. Kiedy po raz pierwszy wprowadzili podzapytania i przez jakiś czas po tym wystąpili z nimi mnóstwo problemów z wydajnością. –

+0

+1, upewnij się też, że masz indeks na spam.textid. – nobody

1

Skopiuj wiersze, które nie są w spam z formularza text do nowej tabeli. Następnie usuń tabelę text i zmień nazwę utworzonej tabeli. Dobrym pomysłem jest nie dodawać żadnych klawiszy do utworzonego stołu. Dodaj klucze po zmianie nazwy.

+0

Serio ??? ..... – Antoniossss

+0

Tak poważnie! Dlaczego o tym nie myślę, najlepsze rozwiązanie w większości praktycznych zastosowań! – taur

5

Twój wybór where id in (select ...) zawsze będzie słaby.

Zamiast używać normalnego dołączyć które będą bardzo wydajny: wybór

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

zawiadomienie od spamu, potem dołączył do tekstu, który daje najlepszą wydajność.

0

Corse zajmie dużo czasu, ponieważ wykonanie podzapytania dla każdego rekordu, ale za pomocą sprzężenia wewnętrznego bezpośrednio ta kwerenda jest wykonywana tylko raz pozwala myśleć, że zapytanie odbędzie

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

ale użycie zapytania join będzie wykonywane tylko jeden raz:

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