如何加快已经使用索引的计数(*)? (MyISAM的)

时间:2009-09-17 06:43:26

标签: sql mysql count myisam

我有一个3个大表(10k,10k和100M行),我试图对它们的连接进行简单计数,其中所有连接的列都被索引。为什么COUNT(*)需要这么长时间,如何加快速度(没有触发器和正在运行的摘要)?

mysql> describe SELECT COUNT(*) FROM `metaward_alias` INNER JOIN `metaward_achiever` ON (`metaward_alias`.`id` = `metaward_achiever`.`alias_id`) INNER JOIN `metaward_award` ON (`metaward_achiever`.`award_id` = `metaward_award`.`id`) WHERE `metaward_award`.`owner_id` = 8;
+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+------+-------------+
| id | select_type | table             | type   | possible_keys                                         | key                        | key_len | ref                             | rows | Extra       |
+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+------+-------------+
|  1 | SIMPLE      | metaward_award    | ref    | PRIMARY,metaward_award_owner_id                       | metaward_award_owner_id    | 4       | const                           | 1552 |             | 
|  1 | SIMPLE      | metaward_achiever | ref    | metaward_achiever_award_id,metaward_achiever_alias_id | metaward_achiever_award_id | 4       | paul.metaward_award.id          | 2498 |             | 
|  1 | SIMPLE      | metaward_alias    | eq_ref | PRIMARY                                               | PRIMARY                    | 4       | paul.metaward_achiever.alias_id |    1 | Using index | 
+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+------+-------------+
3 rows in set (0.00 sec)

但实际运行查询大约需要10分钟,我在MyISAM上,因此表格在此期间完全锁定

3 个答案:

答案 0 :(得分:3)

我想原因是你在三个表上做了很大的连接(没有先应用where子句,结果是10k * 10k * 100M = 10 16 行)。尝试重新排序联接(例如,从metaward_award开始,然后仅加入metaward_achiever查看需要多长时间,然后尝试插入metaward_alias,可能使用子查询来强制您首选的评估顺序)。

如果这没有帮助,您可能必须对数据进行非规范化,例如存储特定metaward_achiever的别名数。然后你就完全摆脱了一个联接。也许您甚至可以缓存metaward_award的总和,具体取决于您的数据更新的频率和频率。

其他可能有帮助的是将所有数据库内容放入RAM: - )

答案 1 :(得分:1)

确保您拥有索引:

metaward_alias      id
metaward_achiever   alias_id
metaward_achiever   award_id
metaward_award      id
metaward_award      owner_id

我相信很多人也会建议指望一个特定的列,但在MySql中,这对您的查询没有任何影响。

<强>更新

您还可以尝试在主表上设置条件,而不是其中一个连接表。这会给你相同的结果,但它可能会更快(我不知道MySql有多聪明):

SELECT COUNT(*) FROM `metaward_award` 
   INNER JOIN `metaward_achiever` 
      ON (`metaward_achiever`.`award_id` = `metaward_award`.`id`) 
   INNER JOIN `metaward_alias` 
      ON (`metaward_alias`.`id` = `metaward_achiever`.`alias_id`) 
WHERE `metaward_award`.`owner_id` = 8

答案 2 :(得分:1)

对于该查询,10分钟太长了。我认为你必须有一个非常小的密钥缓存。您可以使用以下字符获取其大小(

SELECT @@key_buffer_size

首先,您应该运行ANALYZE TABLEOPTIMIZE TABLE。他们会对您的索引进行排序,并可以略微提高性能。

您还应该看看是否可以为列使用更紧凑的类型。例如,如果您不会拥有超过1600万的所有者或奖励或别名,您可以将INT列更改为MEDIUMINT(当然,UNSIGNED)。在某些情况下甚至可能是SMALLINT?这将减少您的索引占用空间,并且您将更多地放入缓存中。