在30 000+行的桌面上优化LEFT JOIN

时间:2010-07-18 09:12:37

标签: mysql query-optimization left-join

我有一个访问者可以发表评论的网站。我想添加回答评论的能力(即嵌套评论)。

起初这个查询速度很快,但是在用现有注释(大约30000)填充表后,会出现一个简单的查询:

SELECT c.id, c2.id
  FROM (SELECT id
         FROM swb_comments
         WHERE pageId = 1411
         ORDER BY id DESC
         LIMIT 10) AS c
  LEFT JOIN swb_comments AS c2 ON c.id = c2.parentId

花了2秒钟,没有儿童评论(!)。

如何优化这样的查询?可能的解决方案是http://www.ferdychristant.com/blog//articles/DOMM-7QJPM7(滚动到“完成平面表模型”)但这会使分页变得相当困难(如何在1个查询中限制10个父评论?)

该表有3个索引,id,pageId和ParentId。

提前致谢!

编辑:

添加了表定义。这是与上述SELECT查询有一些差异的完整定义(即pageId而不是numberId以避免混淆)

CREATE TABLE `swb_comments` (
    `id` mediumint(9) NOT NULL auto_increment,
    `userId` mediumint(9) unsigned NOT NULL default '0',
    `numberId` mediumint(9) unsigned default NULL,
    `orgId` mediumint(9) unsigned default NULL,
    `author` varchar(100) default NULL,
    `email` varchar(255) NOT NULL,
    `message` text NOT NULL,
    `IP` varchar(40) NOT NULL,
    `timestamp` varchar(25) NOT NULL,
    `editedTimestamp` varchar(25) default NULL COMMENT 'last edited timestamp',
    `status` varchar(20) NOT NULL default 'publish',
    `parentId` mediumint(9) unsigned NOT NULL default '0',
    `locale` varchar(10) NOT NULL,
    PRIMARY KEY  (`id`),
    KEY `userId` (`userId`),
    KEY `numberId` (`numberId`),
    KEY `orgId` (`orgId`),
    KEY `parentId` (`parentId`)
  ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=34748 ;

2 个答案:

答案 0 :(得分:1)

问题是如果需要处理派生查询的结果,MySQL无法应用索引(这就是你在possible_keys列中有NULL的原因)。所以我建议过滤掉你需要的十条评论:

SELECT * FROM swb_comments WHERE pageId = 1411 ORDER BY id DESC LIMIT 10

然后发送单独的请求以获取每个评论ID的答案:

SELECT * FROM swb_comments WHERE parentId IN ($commentId1, $commentId2, ..., $commentId10)

在这种情况下,数据库引擎将能够有效地应用pageId和parentId索引。

答案 1 :(得分:0)

如果Fedorenko先生是正确的并且子查询导致了优化者的困难,你能不能尝试......

SELECT c.id, c2.id
    FROM swb_comments c LEFT JOIN swb_comments c2 ON c.id = c2.parentID
    WHERE c.pageId = 1411
    ORDER BY c.id DESC
    LIMIT 10;

看看它是否有任何改善?

后来 - 我使用你的定义创建了一个表,用30,000个骨架行填充它,并尝试了两个查询。它们都在很短的时间内完成。解释计划在这里......

mysql> EXPLAIN SELECT c.id, c2.id
               FROM swb_comments c LEFT JOIN swb_comments c2 ON c.id = c2.parentID
               WHERE c.numberId = 1411     ORDER BY c.id DESC     LIMIT 10;
+----+-------------+-------+------+---------------+----------+---------+------------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key      | key_len | ref        | rows | Extra                       |
+----+-------------+-------+------+---------------+----------+---------+------------+------+-----------------------------+
|  1 | SIMPLE      | c     | ref  | numberId      | numberId | 4       | const      |    1 | Using where; Using filesort |
|  1 | SIMPLE      | c2    | ref  | parentId      | parentId | 3       | books.c.id |   14 |                             |
+----+-------------+-------+------+---------------+----------+---------+------------+------+-----------------------------+

mysql> EXPLAIN SELECT c.id, c2.id
                   FROM swb_comments c LEFT JOIN swb_comments c2 ON c.id = c2.parentID
                   WHERE c.numberId = 1411     ORDER BY c.id DESC     LIMIT 10;
+----+-------------+-------+------+---------------+----------+---------+------------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key      | key_len | ref        | rows | Extra                       |
+----+-------------+-------+------+---------------+----------+---------+------------+------+-----------------------------+
|  1 | SIMPLE      | c     | ref  | numberId      | numberId | 4       | const      |    1 | Using where; Using filesort |
|  1 | SIMPLE      | c2    | ref  | parentId      | parentId | 3       | books.c.id |   14 |                             |
+----+-------------+-------+------+---------------+----------+---------+------------+------+-----------------------------+

正是我所期待的。

这很神秘。

我会考虑一下,看看还有什么我们可以尝试的。