选择速度非常慢的大型MySQL表

选择速度非常慢的大型MySQL表,mysql,Mysql,我在MySQL中有一个很大的表(在MAMP中运行),它有2800万行,大小为3.1GB。这是它的结构 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

我在MySQL中有一个很大的表(在MAMP中运行),它有2800万行,大小为3.1GB。这是它的结构

    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
以下是“显示表格状态”的输出:

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个单独的成本类型-但是当termusage表中没有太多(少于100万)行要计算时,此查询运行正常-但是当termusage表中的行数很大时,将花费很长时间-如何减少选择时间

使用LOAD Data方法从CSV文件每月向termusage表中添加一次数据,因此不需要对插入进行如此优化

编辑:在主查询上显示解释:

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"

您可以尝试删除where子句CONSTRAITING by date,并在select中放入IF语句,这样,如果日期在这些边界内,则返回值,否则返回零值。当然,该和将仅与此范围内的值相加,因为所有其他值都将为零


获取的行数超过您需要的行数听起来有点荒谬,但我们最近在Oracle DB上观察到,这取得了很大的改进。当然,这取决于许多其他因素,但可能值得一试。

您也可以考虑将表分为几年或几个月。因此您有一个termusage\u 2010,termusage\u 2011,…或类似的东西


这不是一个很好的解决方案,但是看到您的表非常大,在较小的服务器上可能会有用。

看起来您在问两个问题-正确吗

第一个查询花费这么长时间的最可能原因是因为它是IO绑定的。从磁盘到MySQL工作台传输1400万条记录需要很长时间

您是否尝试过将第二个查询放在“explain”中?是的,您只返回8行,但是SUM操作可能会对数百万条记录求和


我假设“customer”和“terminal”表被适当地索引了?当您加入termusage的主键时,这应该很快…

感谢您的建议-我将看到一个select语句在没有日期的情况下运行多长时间,只要索引(来自Pelshoff的评论)完成创建!Pelshoff删除了他的答案!@Pelshoff-没问题…没有错误的答案…这让我思考了索引的设置方式!好吧,我很高兴它做了一些事情:思考索引总是一个好主意-您可能想尝试创建一个包含group by子句中所有列的索引,在顺序相同。第一个查询只是为了证明表的速度慢-我实际上没有运行该查询,我只运行最后一个查询…我在问题的最后一个查询上添加了一个显示解释-希望能提供一些答案?酷。解释显示查询没有在termusage表上使用索引,这解释了很多。。。你可以尝试在termid和date列上创建一个索引。OK-我绝对不是MySQL专家-我更像是一个PHP/Java/前端开发人员-我试图找到一个好的资源来解释解释结果-你有吗?或者给我指出正确的方向?@Nevile K-创建一个包含两列的索引?或者创建两个索引(每列1个)抱歉!?!?为两列创建索引-您已经在每个单独的列上创建了索引。“在termusage(termid,date)上创建索引测试”应该可以做到这一点。。。