2011-09-09 110 views
6

這個MySQL查詢運行了大約10個小時,並沒有完成。有什麼是可怕的錯誤。非常緩慢的刪除與子查詢的MySQL基地

這裏有兩個表格(文字和垃圾郵件)。垃圾郵件以我想要刪除的文本存儲垃圾郵件的ID。

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

垃圾郵件只有2列,都是整數。 800K條目的文件大小爲幾Mbs。這兩個int都是主鍵。

文本有3列。 id(prim鍵),文本,標誌。大約1200K條目,大約2.1GB(大多數垃圾郵件)。

該服務器是一個至強四核,2千兆RAM(不問我爲什麼)。只有apache(爲什麼?)和mysqld正在運行。它的舊版免費bsd和mysql 4.1.2(不要問我爲什麼)

線程:6問題:188805慢速查詢:318打開:810刷新表:1打開表格:157每秒查詢平均值: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 

查詢是隻使用一個CPU,頂部說25%的CPU時間(4,以便1)。

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 

任何想法如何解決它?

+0

表上的存儲引擎是什麼? – JamesHalsall

+0

你的查詢包含一個old_id列,但是你對錶'text'的描述不是 - 你真的描述了整個表嗎?總的來說,我懷疑這個問題會奇蹟般地消失在一個更新的MySQL版本中。 –

+1

確保你在'text.old_id'和'spam.textid'上有索引。 – Johan

回答

11

根據我的經驗,子查詢通常是SQL語句中執行時間較慢的原因,因此我儘量避免它們。試試這個:

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

聲明:此查詢未經測試,請先備份! :-)

+0

-1使用隱式SQL語法,走出1989,並使用明確連接代替。這也不能解決問題,因爲你的斷言是不真實的。 OP需要將索引放入連接所涉及的字段中。 – Johan

+0

他的斷言對於那個年份的MySQL版本來說是非常正確的。當他們第一次引入子查詢時,以及之後的一段時間,他們遇到了大量的性能問題。 –

+0

+1,也請確保您有spam.textid的索引。 – nobody

1

將不在spamtext中的行復制到新表中。然後刪除text表並重命名創建的表。 好主意不是添加任何鍵到創建的表。重命名後添加密鑰。

+0

認真??? ..... – Antoniossss

+0

是認真!我爲什麼did'nt想到的是,這裏最好的解決方案在大多數實際應用中迄今爲止! – taur

5

你的選擇where id in (select ...)將始終表現不佳。

相反,使用普通的加入,這將是非常有效的:從垃圾郵件

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

注意選擇,然後再加入到文本,這將提供最佳性能。

0
科西嘉島,因爲它執行對每條記錄的子查詢,將花費大量的時間,但通過內部採用

直接加入該查詢只執行一次 讓想查詢將採取

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

但使用加入查詢將只執行一次:

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