优化MySQL关于聚合的性能

时间:2014-04-07 13:02:23

标签: mysql performance aggregate

我尝试优化报表查询,因为大多数报表查询都会合并聚合。由于桌子的大小相当大且不断增长,我需要倾向于它的表现。

例如,我有一个包含三列的表格:idnameaction。我想计算每个名字所做的行动数量:

SELECT name, COUNT(id) AS count
FROM tbl
GROUP BY name;

尽可能简单,我无法在可接受的时间内运行它。它可能需要30秒,并且没有任何索引,无论如何,我可以添加哪个被考虑在内,但仍会改进它。

当我在上述查询上运行EXPLAIN时,它从不使用表中的任何索引,即name上的索引。

有没有办法提高聚合性能?为什么不使用索引?

[UPDATE]

这是EXPLAIN的输出:

+----+-------------+-------+------+---------------+------+---------+------+---------+----------+-----------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra           |
+----+-------------+-------+------+---------------+------+---------+------+---------+----------+-----------------+
|  1 | SIMPLE      | tbl   | ALL  | NULL          | NULL | NULL    | NULL | 4025567 |   100.00 | Using temporary |
+----+-------------+-------+------+---------------+------+---------+------+---------+----------+-----------------+

以下是表的架构:

CREATE TABLE `tbl` (
  `id` bigint(20) unsigned NOT NULL DEFAULT '0',
  `name` varchar(1000) NOT NULL,
  `action` int unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `inx` (`name`(255))
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2 个答案:

答案 0 :(得分:2)

查询和使用索引的问题在于,您在SELECT语句中引用了两个不同的列,但索引中只有一列,并且在索引上使用了前缀。

试试这个(仅参考名称栏):

SELECT name, COUNT(*) AS count
FROM tbl
GROUP BY name;

使用以下索引(无前缀):

tbl (name)

不要在索引上使用这个查询的前缀,因为如果你这样做,MySQL就不能用它作为覆盖索引(仍然需要点击表格)。

如果使用上述内容,MySQL将扫描name列上的索引,但不必扫描实际的表数据。您应该在解释结果中看到USING INDEX

这与MySQL能够完成这样的任务一样快。另一种方法是单独存储聚合结果,并在数据更改时保持更新。

另外,请考虑减少name列的大小,特别是如果您正在达到索引大小限制,这很可能是您使用前缀的原因。如果你不需要,可以不使用UTF8节省一些空间(UTF8每个字符3个字节用于索引)。

答案 1 :(得分:1)

这是一个非常常见的问题,解决方案的关键在于,你的桌子正在增长。

因此,第一种方法是:如果尚未创建索引,则按name列创建索引。 但是:这将解决您的问题一段时间

更合适的方法是:创建单独的统计表,如

tbl_counts
+------+-------+
| name | count |
+------+-------+

并单独存储您的计数。在tbl表中更改(插入/更新或删除)数据时,您需要调整tbl_counts表中的相应行。这种方式允许您完全摆脱执行COUNT查询 - 但需要在tbl表中添加一些逻辑。

要维护统计信息表的完整性,您可以使用triggers或在应用程序内部执行此操作。如果COUNT查询的性能比您的数据更改查询更重要(但更改tbl_counts表的开销不会太高),此方法很有用