2011-08-17 76 views
1

我有一個MySQL中的大表(在MAMP中運行),它有2800萬行和3.1GB的大小。下面是它的結構大型MySQL表非常慢選擇

CREATE TABLE `termusage` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT, 
    `termid` bigint(20) DEFAULT NULL, 
    `date` datetime DEFAULT NULL, 
    `dest` varchar(255) DEFAULT NULL, 
    `cost_type` tinyint(4) DEFAULT NULL, 
    `cost` decimal(10,3) DEFAULT NULL, 
    `gprsup` bigint(20) DEFAULT NULL, 
    `gprsdown` bigint(20) DEFAULT NULL, 
    `duration` time DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `termid_idx` (`termid`), 
    KEY `date_idx` (`date`), 
    KEY `cost_type_idx` (`cost_type`), 
    CONSTRAINT `termusage_cost_type_cost_type_cost_code` FOREIGN KEY (`cost_type`) REFERENCES `cost_type` (`cost_code`), 
    CONSTRAINT `termusage_termid_terminal_id` FOREIGN KEY (`termid`) REFERENCES `terminal` (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=28680315 DEFAULT CHARSET=latin1 

這裏是SHOW TABLE STATUS輸出:

Name,Engine,Version,Row_format,Rows,Avg_row_length,Data_length,Max_data_length,Index_length,Data_free,Auto_increment,Create_time,Update_time,Check_time,Collation,Checksum,Create_options,Comment  
'termusage', 'InnoDB', '10', 'Compact', '29656469', '87', '2605711360', '0', '2156920832', '545259520', '28680315', '2011-08-16 15:16:08', NULL, NULL, 'latin1_swedish_ci', NULL, '', '' 

我試着運行下面的select語句:

select u.id from termusage u 
    where u.date between '2010-11-01' and '2010-12-01' 

需35分鐘返回結果(大約1400萬行) - 這是使用MySQL Worksbench。

我有以下MySQL的配置設置:

Variable_name    Value 
bulk_insert_buffer_size 8388608 
innodb_buffer_pool_instances 1 
innodb_buffer_pool_size 3221225472 
innodb_change_buffering all 
innodb_log_buffer_size  8388608 
join_buffer_size    131072 
key_buffer_size   8388608 
myisam_sort_buffer_size 8388608 
net_buffer_length    16384 
preload_buffer_size   32768 
read_buffer_size    131072 
read_rnd_buffer_size  262144 
sort_buffer_size    2097152 
sql_buffer_result    OFF 

最終我試着去運行一個大的查詢 - 即加入一個情侶表和小組的一些數據,所有基於變量 - 客戶ID -

select c.id,u.termid,u.cost_type,count(*) as count,sum(u.cost) as cost,(sum(u.gprsup) + sum(u.gprsdown)) as gprsuse,sum(time_to_sec(u.duration)) as duration 
from customer c 
inner join terminal t 
on (c.id = t.customer) 
inner join termusage u 
on (t.id = u.termid) 
where c.id = 1 and u.date between '2011-03-01' and '2011-04-01' group by c.id,u.termid,u.cost_type 

這會返回最多8行(因爲只有8個單獨的cost_types - 但在termusage表中計算的行數不多(少於100萬)的情況下,此查詢運行正常 - 但在術語表中的行數很大 - 如何我可以減少選擇時間嗎?

數據添加到termusage表使用LOAD DATA方法CSV文件每月一次 - 因此它並不需要是這麼調整爲刀片。

編輯:展會上的主要查詢說明:

id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra 
1,SIMPLE,c,const,PRIMARY,PRIMARY,8,const,1,"Using index; Using temporary; Using filesort" 
1,SIMPLE,u,ALL,"termid_idx,date_idx",NULL,NULL,NULL,29656469,"Using where" 
1,SIMPLE,t,eq_ref,"PRIMARY,customer_idx",PRIMARY,8,wlnew.u.termid,1,"Using where" 
+1

顯示來自查詢的說明。 – 2011-08-17 10:36:09

回答

3

看起來像你問了兩個問題 - 正確嗎?

第一個查詢花費這麼長時間的最可能的原因是因爲它是IO限制的。從磁盤傳輸1400萬條記錄並傳輸到MySQL工作臺需要很長時間。

你有沒有嘗試把第二個查詢,雖然「解釋」?是的,你只能得到8行 - 但SUM操作可能會加總數百萬條記錄。

我假設「客戶」和「終端」表適當索引?當你加入關於termusage的主鍵時,這應該是非常快速的......

0

你可以試着按日期刪除where子句限制,而是把一個IF語句中的選擇,這樣,如果日期是這些邊界內,值返回,否則返回零值。當然,SUM將只會求和這個範圍內的值,因爲所有其他值都是零。

這聽起來有點無厘頭比你需要獲取更多行,但我們在Oracle數據庫最近發現,這取得了相當巨大的改進。當然這將取決於許多其他因素,但它可能值得一試。

+0

感謝您的建議 - 我會看到選擇語句在沒有日期的情況下運行多長時間,只要該索引(來自Pelshoff的評論)完成創建! – ManseUK 2011-08-17 10:35:48

+0

Pelshoff刪除了他的答案! – ManseUK 2011-08-17 10:45:21

0

您也可以考慮將表格分成幾年或幾個月。所以你有termusage_2010,termusage_2011,...或者類似的東西。

不是一個很好的解決方案,但看到你的表是相當大的可能是有用的一個較小的服務器上。