Оптимизировать LEFT JOIN на таблице с более чем 30 000 строк - PullRequest
5 голосов
/ 18 июля 2010

У меня есть сайт, где посетители могут оставлять комментарии.Я хочу добавить возможность отвечать на комментарии (т.е. вложенные комментарии).

Сначала этот запрос был быстрым, но после того, как я заполнил таблицу существующими комментариями (около 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 (выделите «Модель плоской таблицы выполнена правильно»), но это делает разбиение на страницы довольно трудным (как я могу ограничить 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 ]

1 голос
/ 18 июля 2010

Проблема в том, что MySQL не может применить индекс, если ему нужно обработать результат из производного запроса (поэтому в столбце возможных_ключей есть NULL). Поэтому я предлагаю отфильтровать десять необходимых вам комментариев:

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

И после этого отправьте отдельный запрос на получение ответов для каждого комментария с идентификатором:

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

В этом случае ядро ​​базы данных сможет эффективно применять индексы pageId и parentId.

0 голосов
/ 18 июля 2010

Если г-н Федоренко прав и подзапрос вызывает трудности с оптимизатором, не могли бы вы попробовать ...

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 |                             |
+----+-------------+-------+------+---------------+----------+---------+------------+------+-----------------------------+

и это именно то, что я ожидал.

Это очень загадочно.

Я подумаю над этимеще немного, чтобы узнать, можем ли мы попробовать что-нибудь еще.

...