为什么大表中的COUNT()查询比SUM()快得多

时间:2013-06-17 13:34:03

标签: mysql sql performance

我有一个包含以下表格的数据仓库:

主要

约800万条记录

CREATE TABLE `main` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`cid` mediumint(8) unsigned DEFAULT NULL, //This is the customer id
`iid` mediumint(8) unsigned DEFAULT NULL, //This is the item id
`pid` tinyint(3) unsigned DEFAULT NULL, //This is the period id
`qty` double DEFAULT NULL,
`sales` double DEFAULT NULL,
`gm` double DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_pci` (`pid`,`cid`,`iid`) USING HASH,
KEY `idx_pic` (`pid`,`iid`,`cid`) USING HASH
) ENGINE=InnoDB AUTO_INCREMENT=7978349 DEFAULT CHARSET=latin1

此表包含约50条记录,并包含以下字段

  • ID

客户

这有大约23,000条记录和以下文件

  • ID
  • number //此字段是唯一的
  • name //这只是一个描述字段

以下查询运行速度非常快(少于1秒)并返回约2,000:

select count(*) 
from mydb.main m 
INNER JOIN mydb.period p ON p.id = m.pid 
INNER JOIN mydb.customer c ON c.id = m.cid 
WHERE p.year = 2013 AND c.number = 'ABC';

但是这个查询要慢得多(小于45秒),这与前一个相同,但总和而不是计数:

select sum(sales)
from mydb.main m 
INNER JOIN mydb.period p ON p.id = m.pid 
INNER JOIN mydb.customer c ON c.id = m.cid 
WHERE p.year = 2013 AND c.number = 'ABC';

当我解释每个查询时,我看到的唯一区别是'count()'  查询'Extra'字段表示'Using index',而对于'sum()'查询,该字段为NULL。

解释count()查询

| id | select_type | table | type  | possible_keys        | key          | key_len | ref                 | rows | Extra       |
|  1 | SIMPLE      | c     | const | PRIMARY,idx_customer | idx_customer | 11      | const               |    1 | Using index |
|  1 | SIMPLE      | p     | ref   | PRIMARY,idx_period   | idx_period   | 4       | const               |    6 | Using index |
|  1 | SIMPLE      | m     | ref   | idx_pci,idx_pic      | idx_pci      | 6       | mydb.p.id,const     |    7 | Using index |

解释sum()查询

| id | select_type | table | type  | possible_keys        | key          | key_len | ref                 | rows | Extra       |
|  1 | SIMPLE      | c     | const | PRIMARY,idx_customer | idx_customer | 11      | const               |    1 | Using index |
|  1 | SIMPLE      | p     | ref   | PRIMARY,idx_period   | idx_period   | 4       | const               |    6 | Using index |
|  1 | SIMPLE      | m     | ref   | idx_pci,idx_pic      | idx_pci      | 6       | mydb.p.id,const     |    7 | NULL        |
  • 为什么count()比sum()快得多?它不应该使用两者的索引吗?
  • 我可以做些什么来使sum()更快?

提前致谢!

修改

所有表格都显示它正在使用Engine InnoDB

另外,作为附注,如果我只是执行“SELECT *”查询,则运行速度非常快(少于2秒)。我希望'SUM()'不应该花费更长的时间,因为SELECT *必须检索行...

解决

这就是我所学到的:

  • 由于销售字段不是索引的一部分,因此必须从硬盘驱动器中检索记录(这可能很慢)。
  • 我对此并不太熟悉,但看起来可以通过切换到SSD(固态驱动器)来提高I / O性能。我将不得不再研究一下。
  • 目前,我想我要创建另一层摘要以获得我正在寻找的性能。
  • 我将主表上的索引重新定义为(pid,cid,iid,sales,gm,qty),现在sum()查询运行速度非常快!

谢谢大家!

2 个答案:

答案 0 :(得分:5)

索引是关键行列表。

执行count()查询时,可以忽略数据库中的实际数据,只使用索引。

当您执行sum(sales)查询时,必须从磁盘读取每一行以获取销售数字,因此要慢得多。

此外,索引可以批量读取,然后在内存中处理,而磁盘访问将随机丢弃驱动器尝试从磁盘读取行。

最后,索引本身可能有计数摘要(以帮助计划生成)

更新

你桌上实际上有三个索引:

PRIMARY KEY (`id`),
KEY `idx_pci` (`pid`,`cid`,`iid`) USING HASH,
KEY `idx_pic` (`pid`,`iid`,`cid`) USING HASH

因此,您只有列idpidcidiid上的索引。 (顺便说一句,大多数数据库都足够智能,可以组合索引,因此您可以稍微优化索引)

如果你添加了另一个像KEY idx_sales(id,sales)这样的可以提高性能的密钥,但考虑到数字上可能的销售价值分布,你会为更新添加额外的性能成本,这可能是一个糟糕的事

答案 1 :(得分:1)

简单的答案是count()只计算行数。这可以通过索引来满足。

sum()需要标识每一行,然后获取该页面以获取sales列。这会增加很多开销 - 每行大约一页加载。

如果您将sales添加到索引中,那么它也应该非常快,因为它不必获取原始数据。