嵌套索引的Mysql性能

时间:2010-08-03 22:25:57

标签: mysql performance indexing

我有一个带有嵌套索引(blog_id,已发布)的mysql表(文章),表现不佳。我在慢速查询日志中看到了很多这些:

- 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行进行排序。

如果要以最佳方式实现它,您将从所有基于blog-id的流的集合中放入一个堆,并选择具有max(已发布)的那个并重复200次。每个操作都应该很快。

自从Google,Facebook,Twitter,微软和所有大公司都在使用mysql进行制作时,我肯定会遗漏一些东西。有经验的人吗?

编辑:按照thieger的回答更新。我尝试了索引提示,它似乎没有帮助。结果如下,最后附上。 Mysql order by optimisation声称要解决他们提出的问题:

  

我同意MySQL可能会使用   复合blog_id-published-index,   但仅限于blog_id部分   查询。

     

SELECT * FROM t1 WHERE   key_part1 =常数ORDER BY   key_part2;

Atleast mysql似乎声称它可以在WHERE子句(blog_id部分查询)之外使用。任何帮助theiger?

谢谢, -Prasanna [myprasanna at gmail dot com]

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 个答案:

答案 0 :(得分:3)

您是否尝试使用EXPLAIN来查看您的索引是否完全使用?您是否已分析更新索引统计信息?

我同意MySQL可能会使用复合blog_id-published-index,但仅限于查询的blog_id部分。如果在ANALYZE之后没有使用索引,你可以尝试给MySQL提供USE INDEX甚至FORCE INDEX的提示,但MySQL优化器也可以正确地假设顺序扫描比使用索引更快。对于您的查询类型,我还建议在category_id和blog_id上添加索引并尝试使用它。

答案 1 :(得分:1)

除了thieger的优秀答案,您可能还想查看:

  • 如果(category_id,blog_id,published)上的索引有用。
  • 如果有足够的空间将所有索引保存在内存中(例如,innodb缓冲池使用和刷新,mysqlreport在这方面是一个非常方便的工具)

答案 2 :(得分:0)

MySQL有一个截断机制,如果它检测到它可能不得不查看超过表的三分之一,它将不会使用索引。由于看起来您的查询将匹配8000多行行表的超过6000行,这绝对是正在发生的事情。

此外,MySQL通常不能在同一个表上使用两次索引,也不能使用多个索引。在这种情况下,它不会使用ORDER BY子句的索引,因为它指定的列不同于WHERE子句中的列。