优化MySQL查询,几乎需要20秒!
我正在Macbook Pro 2.53ghz和4GB内存上运行以下查询:优化MySQL查询,几乎需要20秒!,mysql,performance,optimization,indexing,Mysql,Performance,Optimization,Indexing,我正在Macbook Pro 2.53ghz和4GB内存上运行以下查询: SELECT c.id AS id, c.name AS name, c.parent_id AS parent_id, s.domain AS domain_name, s.domain_id AS domain_id, NULL AS stats FROM stats s
SELECT
c.id AS id,
c.name AS name,
c.parent_id AS parent_id,
s.domain AS domain_name,
s.domain_id AS domain_id,
NULL AS stats
FROM
stats s
LEFT JOIN stats_id_category sic ON s.id = sic.stats_id
LEFT JOIN categories c ON c.id = sic.category_id
GROUP BY
c.name
大约需要17秒才能完成
说明:
各表:
资料:
Number of rows: 147397
Data size: 20.3MB
Index size: 1.4MB
表:
CREATE TABLE `stats` (
`id` int(11) unsigned NOT NULL auto_increment,
`time` int(11) NOT NULL,
`domain` varchar(40) NOT NULL,
`ip` varchar(20) NOT NULL,
`user_agent` varchar(255) NOT NULL,
`domain_id` int(11) NOT NULL,
`date` timestamp NOT NULL default CURRENT_TIMESTAMP,
`referrer` varchar(400) default NULL,
KEY `id` (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=147398 DEFAULT CHARSET=utf8
信息第二表:
Number of rows: 1285093
Data size: 11MB
Index size: 17.5MB
CREATE TABLE `stats_id_category` (
`stats_id` int(11) NOT NULL,
`category_id` int(11) NOT NULL,
KEY `stats_id` (`stats_id`,`category_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
第二张表:
Number of rows: 1285093
Data size: 11MB
Index size: 17.5MB
CREATE TABLE `stats_id_category` (
`stats_id` int(11) NOT NULL,
`category_id` int(11) NOT NULL,
KEY `stats_id` (`stats_id`,`category_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
信息第三表:
Number of rows: 161
Data size: 3.9KB
Index size: 8KB
CREATE TABLE `categories` (
`id` int(11) NOT NULL auto_increment,
`parent_id` int(11) default NULL,
`name` varchar(40) NOT NULL,
`questions_category_id` int(11) NOT NULL default '0',
`rank` int(2) NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `id` (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=205 DEFAULT CHARSET=latin1
第三张表:
Number of rows: 161
Data size: 3.9KB
Index size: 8KB
CREATE TABLE `categories` (
`id` int(11) NOT NULL auto_increment,
`parent_id` int(11) default NULL,
`name` varchar(40) NOT NULL,
`questions_category_id` int(11) NOT NULL default '0',
`rank` int(2) NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `id` (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=205 DEFAULT CHARSET=latin1
希望有人能帮我加快速度。您缺少所提供信息(类别)中的第三个表格 另外,您正在执行左连接,然后在GROUPBY中使用右表(可能都是null),这似乎很奇怪。结果,您将把所有不匹配的行分组在一起,这是您想要的吗
最后,你能解释一下这个选择吗?哈里森是对的;我们需要另一张桌子。不过,我会先将category\u id上的索引添加到stats\u id\u category中。我在您的查询中看到几个WTF:
LEFT-OUTER-JOIN
s,然后按可能不匹配的c.name
列进行分组。也许你真的不需要外部连接?如果是这种情况,您应该使用内部联接,因为外部联接通常较慢c.name
进行分组,但这会对选择列表中的每一列给出不明确的结果。即,c.name
在每个分组的这些列中可能有多个值。幸运的是,您使用的是MySQL,因为此查询只会在任何其他RDBMS中给出一个错误
这是一个性能问题,因为分组依据
可能导致您在解释中看到的“使用临时;使用文件排序”。这是一个臭名昭著的性能杀手,这可能是这个查询花费17秒的最大原因。由于根本不清楚为什么要使用groupby
(不使用聚合函数,并且违反了单值规则),因此您似乎需要重新考虑这一点
c.name
进行分组,它没有UNIQUE
约束。从理论上讲,你可以有多个同名的类别,这些类别可以集中在一个组中。我想知道如果你想要每个类别一组,为什么不按c.id
分组选择NULL作为stats
:我不明白你为什么需要这个。这有点像创建了一个从未使用过的变量。它不应该损害性能,但它只是另一个WTF,让我觉得您没有很好地理解这个查询SUM()
或COUNT()
。您的选择列表包括s.domain
和s.domain\u id
,这对每个访问者都是不同的,对吗?那么,如果每个类别只有一行,那么您希望结果集中有什么值呢?这实际上也不是性能问题,它只是意味着查询结果没有告诉您任何有用的信息stats\u id\u category
表的两列上都有索引,但没有主键。因此,您很容易获得重复的行,这意味着您的访客计数可能不准确。您需要删除该冗余索引并改用主键。我会先在主键中排序category\u id
,这样连接就可以利用索引
ALTER TABLE stats_id_category DROP KEY stats_id,
ADD PRIMARY KEY (category_id, stats_id);
SELECT c.id, c.name, c.parent_id, COUNT(*) AS num_visitors
FROM categories c
INNER JOIN stats_id_category sic ON (sic.category_id = c.id)
GROUP BY c.id;
现在查询根本不需要读取
stats
表,甚至不需要读取stats\u id\u category表。它只需读取stats\u id\u category
表的索引就可以得到它的计数,这应该可以省去很多工作。我同意比尔的观点。第二点非常重要。这个查询甚至没有逻辑意义。此外,没有where语句这一简单事实意味着您必须收回统计表中的每一行,它似乎在140000左右。然后,它必须对所有这些数据进行排序,以便能够按顺序执行分组。这是因为排序[O(n log n)]然后查找重复项[O(n)]比只查找重复项而不排序数据集[O(n^2)??]快得多 你的服务器规格是什么?17秒对于将100K行与1M行连接起来可能不是太糟糕。请包含EXPLAIN的输出。您可能希望使用limit并成批处理您的输出。你对这么多的记录做了什么样的处理?包括解释,包括服务器规格。我用它来生成静力学。每个类别的访问者数量和此类统计。我添加了第三个表、我的系统信息和一个解释。谢谢分类id和统计id(一起)上已经有索引。或者将它们分开会更好吗?或者你正在使用索引键进行搜索,或者你没有。如果您有一个复合键,除非您同时搜索这两个字段,否则它不会使用索引。