我有一个带有嵌套索引(blog_id,已发布)的 mysql 表(文章),并且性能很差。我在慢查询日志中看到很多这样的内容:
- 查询时间:23.184007 锁定时间:0.000063 已发送行数:380 已检查行数:6341
从文章中选择 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)按发布的 DESC LIMIT 380 排序;
我无法理解为什么 mysql 会运行带有这些 blog_ids 的所有行来找出我的前 380 行。我希望嵌套索引的全部目的是加快速度。至少,即使是一个简单的实现,也应该按 blog_id 查找并获取按已发布排序的前 380 行。这应该很快,因为由于嵌套索引,我们可以计算出确切的 200 行。然后对得到的 19*200=3800 行进行排序。
如果要以最优化的方式实现它,您可以从所有基于 blog-id 的流集中放入一个堆,然后选择具有 max(published) 的流并重复 200 次。每个操作都应该很快。
自从 Google、Facebook、Twitter、Microsoft 和所有大公司都将 mysql 用于生产目的以来,我肯定错过了一些东西。有哪位有经验的吗?
编辑:根据 thieger 的回答进行更新。我尝试过索引提示,但似乎没有帮助。结果附在下面的最后。 Mysql order by optimisation声称要解决 theiger 提出的问题:
I agree that MySQL might possibly use the composite blog_id-published-index, but only for the blog_id part of the query.
SELECT * FROM t1 WHERE key_part1=constant ORDER BY key_part2;
至少 mysql 似乎声称它的用途不仅仅限于 WHERE 子句(查询的 blog_id 部分)。有帮助吗?
谢谢, -普拉桑那 [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)
最佳答案
您是否尝试过 EXPLAIN 来查看您的索引是否已被使用?您是否已分析更新索引统计信息?
我同意 MySQL 可能会使用复合 blog_id-published-index,但仅限于查询的 blog_id 部分。如果在 ANALYZE 之后没有使用索引,你可以尝试用 USE INDEX 甚至 FORCE INDEX 给 MySQL 一个提示,但是 MySQL 优化器也可能正确地认为顺序扫描比使用索引更快。对于您的查询类型,我还建议在category_id和blog_id上添加索引并尝试使用它。
关于嵌套索引的 MySQL 性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3401236/