我尝试优化报表查询,因为大多数报表查询都会合并聚合。由于桌子的大小相当大且不断增长,我需要倾向于它的表现。
例如,我有一个包含三列的表格:id
,name
,action
。我想计算每个名字所做的行动数量:
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;
答案 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
表的开销不会太高),此方法很有用