用大量数据可以更快地执行[MySQL]

时间:2012-04-24 13:09:47

标签: mysql optimization high-load sql-optimization

有什么方法可以优化下一个查询:

EXPLAIN EXTENDED SELECT keyword_id, ck.keyword, COUNT( article_id ) AS cnt
FROM career_article_keyword
LEFT JOIN career_keywords ck
USING ( keyword_id ) 
WHERE keyword_id
IN (

SELECT keyword_id
FROM career_article_keyword
LEFT JOIN career_keywords ck
USING ( keyword_id ) 
WHERE article_id
IN (

SELECT article_id
FROM career_article_keyword
WHERE keyword_id =9
)
AND keyword_id <>9
)
GROUP BY keyword_id
ORDER BY cnt DESC

如果我有特定的keyword_id(CURRENT_KID)这里的主要任务我需要找到所有与CURRENT_KID一起属于任何文章的关键字,并根据使用数量这些关键字排序结果

表定义为:

mysql> show create table career_article_keyword;
+------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table                  | Create Table                                                                                                                                                                                                                                                                                                                                               |
+------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| career_article_keyword | CREATE TABLE `career_article_keyword` (
  `article_id` int(11) unsigned NOT NULL,
  `keyword_id` int(11) NOT NULL,
  UNIQUE KEY `article_id` (`article_id`,`keyword_id`),
  CONSTRAINT `career_article_keyword_ibfk_1` FOREIGN KEY (`article_id`) REFERENCES `career` (`menu_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> show create table career_keywords;
+-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table           | Create Table                                                                                                                                                                                                         |
+-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| career_keywords | CREATE TABLE `career_keywords` (
  `keyword_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `keyword` varchar(250) NOT NULL,
  PRIMARY KEY (`keyword_id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 |
+-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

“解释”的输出让我害怕

http://o7.no/J6ThIs

在大数据上这个查询可以杀死所有东西:)我可以以某种方式加快速度吗?

感谢。

1 个答案:

答案 0 :(得分:2)

查看您的EXPLAIN输出,我担心您使用子查询导致索引使用次优。我感觉(没有任何理由 - 并且我可能非常错误)使用JOIN重写可能会导致更优化的查询。

为此,我们需要了解您的查询要执行的操作。如果你的问题明确表达了它会有所帮助,但是经过一些小小的讨论之后我决定你的查询试图获取包含某个给定关键字的任何文章中出现的所有其他关键字的列表,以及计数这些关键字出现的所有文章

现在让我们分阶段重建查询:

  1. 获取“任何包含某个给定关键字的文章”(不用担心重复):

    SELECT ca2.article_id
    FROM
           career_article_keyword AS ca2
    WHERE
          ca2.keyword_id = 9;
    
  2. 获取“出现在[上述]中的所有其他关键字

    SELECT ca1.keyword_id
    FROM
           career_article_keyword AS ca1
      JOIN career_article_keyword AS ca2 ON (ca2.article_id = ca1.article_id)
    WHERE
          ca1.keyword_id <> 9
      AND ca2.keyword_id =  9
    GROUP BY ca1.keyword_id;
    
  3. 获取“ [以上],以及这些关键字出现的所有文章的计数

    SELECT ca1.keyword_id, COUNT(DISTINCT ca0.article_id) AS cnt
    FROM
           career_article_keyword AS ca0
      JOIN career_article_keyword AS ca1 USING (keyword_id)
      JOIN career_article_keyword AS ca2 ON (ca2.article_id = ca1.article_id)
    WHERE
          ca1.keyword_id <> 9
      AND ca2.keyword_id =  9
    GROUP BY ca1.keyword_id
    ORDER BY cnt DESC;
    
  4. 最后,我们要在career_keyword表中添加匹配关键字本身的输出:

    SELECT ck.keyword_id, ck.keyword, COUNT(DISTINCT ca0.article_id) AS cnt
    FROM
           career_keywords        AS ck 
      JOIN career_article_keyword AS ca0 USING (keyword_id)
      JOIN career_article_keyword AS ca1 USING (keyword_id)
      JOIN career_article_keyword AS ca2 ON (ca2.article_id = ca1.article_id)
    WHERE
          ca1.keyword_id <> 9
      AND ca2.keyword_id =  9
    GROUP BY ck.keyword_id -- equal to ca1.keyword_id due to join conditions
    ORDER BY cnt DESC;
    
  5. 立即明确的一件事是你的原始查询引用career_keywords两次,而这个重写的查询仅引用该表一次;仅这一点可以解释性能差异 - 尝试删除对它的第二个引用(即它出现在你的第一个子查询中),因为它在那里完全是多余的。

    回顾这个查询,我们可以看到正在对以下列执行连接:

      career_keywords.keyword_id

    • ck JOIN ca0

      此表定义PRIMARY KEY (`keyword_id`),因此有一个很好的索引可用于此连接。

    • career_article_keyword.article_id

    • ca1 JOIN ca2

      此表定义UNIQUE KEY `article_id` (`article_id`,`keyword_id`),并且由于article_id是此索引中最左侧的列,因此有一个可用于此连接的良好索引。

    • career_article_keyword.keyword_idck JOIN ca0

    • ca0 JOIN ca1

      没有可用于此连接的索引:此表中定义的唯一索引具有article_id左侧的另一列keyword_id - 因此MySQL无法找到keyword_id索引中的条目,而不是先知道article_id。我建议你创建一个新的索引,其最左边的列是keyword_id

      (同样可以直接通过查看原始查询来确定对此索引的需求,其中您的两个最外层查询在该列上执行连接。)