Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/71.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
优化MySQL查询,几乎需要20秒!_Mysql_Performance_Optimization_Indexing - Fatal编程技术网

优化MySQL查询,几乎需要20秒!

优化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

我正在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
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(一起)上已经有索引。或者将它们分开会更好吗?或者你正在使用索引键进行搜索,或者你没有。如果您有一个复合键,除非您同时搜索这两个字段,否则它不会使用索引。