Кто-нибудь хочет помочь оптимизировать запрос MySQL? - PullRequest
1 голос
/ 09 июля 2009

Вот запрос:

SELECT COUNT(*) AS c, MAX(`followers_count`) AS max_fc, 
       MIN(`followers_count`) AS min_fc, MAX(`following_count`) AS max_fgc,
       MIN(`following_count`) AS min_fgc, SUM(`followers_count`) AS fc,
       SUM(`following_count`) AS fgc, MAX(`updates_count`) AS max_uc,
       MIN(`updates_count`) AS min_uc, SUM(`updates_count`) AS uc
FROM `profiles`
WHERE `twitter_id` IN (SELECT `followed_by` 
                       FROM `relations` 
                       WHERE `twitter_id` = 123);

Две таблицы: profiles и relations. Оба имеют более 1 000 000 строк, двигатель InnoDB. Оба имеют индексы на twitter_id, relations имеет дополнительный индекс на (twitter_id, followed_by). Выполнение запроса занимает более 6 секунд, это действительно расстраивает меня. Я знаю, что могу как-то ПРИСОЕДИНЯТЬСЯ к этому, но мои знания MySQL не так хороши, поэтому я прошу вашей помощи.

Спасибо всем заранее =)

Cheers, К ~

Обновлено

Хорошо, мне удалось снизить скорость до 2,5 секунд. Я использовал INNER JOIN и добавил три пары индексов. Вот EXPLAIN результаты:

id, select_type, table, type, possible_keys, 
    key, key_len, ref, rows, Extra

1, 'SIMPLE', 'r', 'ref', 'relation', 
    'relation', '4', 'const', 252310, 'Using index'

1, 'SIMPLE', 'p', 'ref', 'PRIMARY,twiter_id,id_fc,id_fgc,id_uc', 
    'id_uc', '4', 'follerme.r.followed_by', 1, ''

Надеюсь, это поможет.

Еще одно обновление

Вот операторы SHOW CREATE TABLE для обеих таблиц:

CREATE TABLE `profiles` (
  `twitter_id` int(10) unsigned NOT NULL,
  `screen_name` varchar(45) NOT NULL default '',
  `followers_count` int(10) unsigned default NULL,
  `following_count` int(10) unsigned default NULL,
  `updates_count` int(10) unsigned default NULL,
  `location` varchar(45) default NULL,
  `bio` varchar(160) default NULL,
  `url` varchar(255) default NULL,
  `image` varchar(255) default NULL,
  `registered` int(10) unsigned default NULL,
  `timestamp` int(10) unsigned default NULL,
  `relations_timestamp` int(10) unsigned default NULL,
  PRIMARY KEY  USING BTREE (`twitter_id`,`screen_name`),
  KEY `twiter_id` (`twitter_id`),
  KEY `screen_name` USING BTREE (`screen_name`,`twitter_id`),
  KEY `id_fc` (`twitter_id`,`followers_count`),
  KEY `id_fgc` (`twitter_id`,`following_count`),
  KEY `id_uc` (`twitter_id`,`updates_count`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

CREATE TABLE `relations` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `twitter_id` int(10) unsigned NOT NULL default '0',
  `followed_by` int(10) unsigned default NULL,
  `timestamp` int(10) unsigned default NULL,
  PRIMARY KEY  USING BTREE (`id`,`twitter_id`),
  UNIQUE KEY `relation` (`twitter_id`,`followed_by`)
) ENGINE=InnoDB AUTO_INCREMENT=1209557 DEFAULT CHARSET=utf8

Ух, какой беспорядок =) Извините!

Ответы [ 5 ]

3 голосов
/ 09 июля 2009

Объединение будет выглядеть примерно так:

SELECT COUNT(*) AS c,
MAX(p.`followers_count`) AS max_fc,
MIN(p.`followers_count`) AS min_fc,
MAX(p.`following_count`) AS max_fgc,
MIN(p.`following_count`) AS min_fgc,
SUM(p.`followers_count`) AS fc,
SUM(p.`following_count`) AS fgc,
MAX(p.`updates_count`) AS max_uc,
MIN(p.`updates_count`) AS min_uc,
SUM(p.`updates_count`) AS uc
FROM `profiles` AS p
INNER JOIN `relations` AS r ON p.`twitter_id` = r.`followed_by`
WHERE r.`twitter_id` = 123;

Чтобы помочь оптимизировать его, вы должны запустить EXPLAIN SELECT ... для обоих запросов.

1 голос
/ 09 июля 2009

Я бы подошел к этой проблеме с точки зрения программистов; У меня была бы отдельная таблица (или область хранения где-нибудь), в которой хранятся значения max, min и sum, связанные с каждым полем в исходном запросе, и я обновляю эти значения каждый раз, когда я обновляю и добавляю запись в таблицу. (хотя удаление может быть проблематичным, если не обрабатывается правильно).

После того, как исходный запрос для заполнения этих значений завершен (что практически совпадает с запросом, который вы разместили), вы существенно сокращаете свой окончательный запрос до получения одной строки из таблицы данных, а не вычисляете все все в один раз.

1 голос
/ 09 июля 2009

count (*) - это очень дорогая операция в движке InnoDB, пробовали ли вы этот запрос без этой части? Если это вызывает наибольшее время обработки, то, возможно, вы могли бы сохранить текущее значение вместо того, чтобы каждый раз запрашивать его.

1 голос
/ 09 июля 2009

Создайте следующие составные индексы:

profiles (twitter_id, followers_count)
profiles (twitter_id, following_count)
profiles (twitter_id, updates_count)

и опубликовать план запроса, ради всего святого.

Кстати, сколько строк возвращает COUNT(*)?

Обновление:

Ваши строки таблицы довольно длинные. Создайте составной индекс для всех выбранных вами полей:

profiles (twitter_id, followers_count, following_count, updates_count)

, чтобы запрос JOIN мог получить все необходимые значения из этого индекса.

1 голос
/ 09 июля 2009
SELECT COUNT(*) AS c,
  MAX(`followers_count`) AS max_fc, MIN(`followers_count`) AS min_fc,
  MAX(`following_count`) AS max_fgc, MIN(`following_count`) AS min_fgc,
  SUM(`followers_count`) AS fc, SUM(`following_count`) AS fgc,
  MAX(`updates_count`) AS max_uc, MIN(`updates_count`) AS min_uc, SUM(`updates_count`) AS uc
FROM `profiles`
JOIN `relations`
  ON (profiles.twitter_id = relations.followed_by)
WHERE relations.twitted_id = 123;

может быть немного быстрее, но вам нужно измерить и проверить, действительно ли это так.

...