Производительность Mysql с вложенными индексами - PullRequest
2 голосов
/ 04 августа 2010

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

- Query_time: 23.184007 Lock_time: 0.000063 Rows_sent: 380 Rows_examined: 6341 SELECT id from articles WHERE category_id = 11 AND blog_id IN (13,14,15,16,17,18,19,20,21,22,23,24,26,27,6330,6331,8269,12218,18889) order by published DESC LIMIT 380;

У меня проблемы с пониманием, почему mysql пробежал все строк с этими blog_ids, чтобы выяснить мои лучшие 380 строк. Я ожидаю, что вся цель вложенного индекса - ускорить его. По крайней мере, даже наивная реализация должна искать по blog_id и получать ее первые 380 строк, упорядоченных по опубликованным. Это должно быть быстро, так как мы можем вычислить точные 200 строк, благодаря вложенному индексу. А затем отсортировать получившиеся 19 * 200 = 3800 строк.

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

Я определенно что-то упускаю, поскольку Google, Facebook, Twitter, Microsoft и все крупные компании используют mysql для производственных целей. Кто-нибудь с опытом?

Редактировать: Обновление в соответствии с ответом Тигера. Я попробовал намеки на указатель, но это не помогло. Результаты прилагаются ниже, в конце. Mysql заказ по оптимизации утверждает, что решает проблему, которую поднимает theiger:

Я согласен, что MySQL может использовать составной блог_ид-опубликованного-индекса, но только для части blog_id запрос.

ВЫБРАТЬ * ОТ t1 ГДЕ key_part1 = константа ORDER BY key_part2;

Atleast mysql, похоже, утверждает, что его можно использовать не только в предложении WHERE (часть запроса blog_id). Любая помощь theiger?

Спасибо, -Prasanna [myprasanna в Gmail точка ком]

CREATE TABLE IF NOT EXISTS `articles` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `category_id` int(11) DEFAULT NULL,
  `blog_id` int(11) DEFAULT NULL,
  `cluster_id` int(11) DEFAULT NULL,
  `title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `description` text COLLATE utf8_unicode_ci,
  `keywords` text COLLATE utf8_unicode_ci,
  `image_url` varchar(511) COLLATE utf8_unicode_ci DEFAULT NULL,
  `url` varchar(511) COLLATE utf8_unicode_ci DEFAULT NULL,
  `url_hash` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
  `author` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `categories` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `published` int(11) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `is_image_crawled` tinyint(1) DEFAULT NULL,
  `image_candidates` text COLLATE utf8_unicode_ci,
  `title_hash` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
  `article_readability_crawled` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_articles_on_url_hash` (`url_hash`),
  KEY `index_articles_on_cluster_id` (`cluster_id`),
  KEY `index_articles_on_published` (`published`),
  KEY `index_articles_on_is_image_crawled` (`is_image_crawled`),
  KEY `index_articles_on_category_id` (`category_id`),
  KEY `index_articles_on_title_hash` (`title_hash`),
  KEY `index_articles_on_article_readability_crawled` (`article_readability_crawled`),
  KEY `index_articles_on_blog_id` (`blog_id`,`published`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=562907 ;

SELECT id from articles USE INDEX(index_articles_on_blog_id) WHERE category_id = 11 AND blog_id IN (13,14,15,16,17,18,19,20,21,22,23,24,26,27,6330,6331,8269,12218,18889) order by published DESC LIMIT 380;

....
380 rows in set (11.27 sec)

explain SELECT id from articles USE INDEX(index_articles_on_blog_id) WHERE category_id = 11 AND blog_id IN (13,14,15,16,17,18,19,20,21,22,23,24,26,27,6330,6331,8269,12218,18889) order by published DESC LIMIT 380\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: articles
         type: range
possible_keys: index_articles_on_blog_id
          key: index_articles_on_blog_id
      key_len: 5
          ref: NULL
         rows: 8640
        Extra: Using where; Using filesort
1 row in set (0.00 sec)

Ответы [ 3 ]

3 голосов
/ 04 августа 2010

Вы пробовали EXPLAIN, чтобы увидеть, используется ли ваш индекс вообще? Вы АНАЛИЗИРУЛИ обновить статистику индекса?

Я согласен, что MySQL может использовать составной blog_id-опубликованный-индекс, но только для части запроса blog_id. Если индекс не используется после ANALYZE, вы можете попробовать дать MySQL подсказку с помощью USE INDEX или даже FORCE INDEX, но оптимизатор MySQL может также правильно предположить, что последовательное сканирование выполняется быстрее, чем использование индекса. Для вашего запроса я бы также предложил добавить индекс для category_id и blog_id и попытаться использовать его.

1 голос
/ 04 августа 2010

Помимо отличного ответа thieger, вы также можете проверить:

  • , если какой-либо индекс для (category_id,blog_id,published) используется.
  • , если достаточно места, чтобы сохранитьиндексы в памяти (например, использование пула буферов innodb и сбросы, mysqlreport - очень удобный инструмент в этом отношении)
0 голосов
/ 04 августа 2010

MySQL имеет механизм отсечения, при котором, если он обнаружит, что ему, вероятно, придется просмотреть более трети таблицы, он не будет использовать индекс.Поскольку кажется, что ваш запрос будет соответствовать чуть более 6000 строк таблицы с нечетным числом строк 8000, это определенно то, что происходит.

Кроме того, MySQL обычно не может использовать индекс дважды для одной и той же таблицы, равно какможно ли использовать более одного.В этом случае он не будет использовать индекс для предложения ORDER BY, поскольку в нем указаны столбцы, отличные от указанного в предложении WHERE.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...