我有一个带有嵌套索引(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)
答案 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)
上的索引有用。mysqlreport
在这方面是一个非常方便的工具)答案 2 :(得分:0)
MySQL有一个截断机制,如果它检测到它可能不得不查看超过表的三分之一,它将不会使用索引。由于看起来您的查询将匹配8000多行行表的超过6000行,这绝对是正在发生的事情。
此外,MySQL通常不能在同一个表上使用两次索引,也不能使用多个索引。在这种情况下,它不会使用ORDER BY
子句的索引,因为它指定的列不同于WHERE
子句中的列。