【问题标题】:Mysql performance with nested indices具有嵌套索引的 Mysql 性能
【发布时间】:2011-03-24 23:51:51
【问题描述】:

我有一个带有嵌套索引(blog_id,published)的 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_id 遍历 all 行来找出我的前 380 行。我希望嵌套索引的全部目的是加快速度。至少,即使是一个幼稚的实现,也应该通过 blog_id 查找并获得按已发布排序的前 380 行。这应该很快,因为由于嵌套索引,我们可以计算出准确的 200 行。然后对生成的 19*200=3800 行进行排序。

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

自从 Google、Facebook、Twitter、Microsoft 和所有大公司都将 mysql 用于生产目的后,我肯定会遗漏一些东西。有经验的吗?

编辑:根据蒂格的回答进行更新。我尝试了索引提示,但似乎没有帮助。结果附在下面,最后。 Mysql order by optimisation 声称解决了他们提出的问题:

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

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

至少 mysql 似乎声称它可以在 WHERE 子句(查询的 blog_id 部分)之外使用。有什么帮助吗?

谢谢, -Prasanna [在 gmail dot com 上的 myprasanna]

如果不存在`articles`,则创建表( `id` int(11) NOT NULL AUTO_INCREMENT, `category_id` int(11) 默认为 NULL, `blog_id` int(11) 默认为空, `cluster_id` int(11) 默认为 NULL, `title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `description` 文本 COLLATE utf8_unicode_ci, `keywords` 文本 COLLATE utf8_unicode_ci, `image_url` varchar(511) 整理 utf8_unicode_ci DEFAULT NULL, `url` varchar(511) 整理 utf8_unicode_ci DEFAULT NULL, `url_hash` varchar(50) 整理 utf8_unicode_ci DEFAULT NULL, `author` varchar(255) 整理 utf8_unicode_ci DEFAULT NULL, `categories` varchar(255) 整理 utf8_unicode_ci DEFAULT NULL, `已发布` int(11) 默认为 NULL, `created_at` 日期时间默认为 NULL, `updated_at` 日期时间 DEFAULT NULL, `is_image_crawled` tinyint(1) 默认为 NULL, `image_candidates` 文本整理 utf8_unicode_ci, `title_hash` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `article_readability_crawled` tinyint(1) 默认为 NULL, 主键(`id`), KEY `index_articles_on_url_hash` (`url_hash`), KEY `index_articles_on_cluster_id`(`cluster_id`), KEY `index_articles_on_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`) ) 引擎=InnoDB 默认字符集=utf8 排序=utf8_unicode_ci AUTO_INCREMENT=562907 ; 从文章中选择 id 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) 按已发布的 DESC LIMIT 380 排序; …… 380 行(11.27 秒) 从文章中解释 SELECT id 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) 按已发布的 DESC LIMIT 380\G 排序; ****************************** 1. 行 ************************ ******* 编号:1 选择类型:简单 表:文章 类型:范围 可能键:index_articles_on_blog_id 键:index_articles_on_blog_id 关键长度:5 参考:空 行数:8640 额外:使用where;使用文件排序 一组中的 1 行(0.00 秒)

【问题讨论】:

    标签: mysql performance indexing


    【解决方案1】:

    您是否尝试过 EXPLAIN 来查看您的索引是否被使用?您是否通过 ANALYZE 更新索引统计信息?

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

    【讨论】:

    • 另外我忘了说,blog_id和cateogory_id有唯一的关联,可以去掉查询的category_id = xxx部分。所以在任何索引中包含 category_id 似乎没有意义。
    • 还通过编辑更新了问题。请看一下。感谢您的回复。
    • 至于“与 category_id 的唯一关联”,我不确定您的意思,但如果 MySQL 不知道,无论如何也没关系。关于使用索引排序,再想一想:如果索引是按照blog_id排序,然后是published,而你让MySQL选择多个blog_ids的记录范围,那么结果不可能已经按照published排序,所以它有再次排序。但我也对您的 EXPLAIN 输出感到困惑,MySQL 声称它使用索引但仍考虑所有记录——或者是否有超过 8000 条记录?在第一个输出中它只有 6000。
    • 只是为了看看这种极端情况,如果我做 LIMIT 1,mysql 会获取所有数千行并对它们进行排序吗?当您与 blog_id 相交时,您拥有的额外信息是发布的顺序。但似乎mysql没有这样做。无论如何,我会将这个问题标记为已回答。谢谢,干杯。
    【解决方案2】:

    除了thieger 的出色回答,您可能还想检查一下:

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

    【讨论】:

    • 另外我忘了说,blog_id和cateogory_id有唯一的关联,可以去掉查询的category_id = xxx部分。所以在任何索引中包含 category_id 似乎没有意义。我已经更新了问题,请看一下。谢谢。
    • 那么,没有 category_id 的查询会做什么?你的 innodb 密钥状态如何?
    【解决方案3】:

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

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

    【讨论】:

    • 从 Prasanna 的编辑中可以看出,MySQL 实际上使用了索引。 (除了他说 MySQL 检查所有行——大约 6000 行——我们不知道该表有多少行)。而且,正如 Prasanna 正确指出的那样,在某些情况下,索引可用于 where 和 order by part。似乎这不是这样的查询,可能是因为“in (...)”在此处所需的意义上不是一个常数。
    • 啊...是的,你是对的:它正在使用索引。我还误读了解释中的“行”列。我实际上同意 Prasanna 关于索引的链接。按照他的查询,MySQL 不会在ORDER BY 子句中使用索引。我可能没有尽可能清楚这一点。 IME,大多数遇到此类问题的人都需要一段时间才能意识到ORDER BY 需要引用与WHERE 子句中相同的列才能使用索引进行排序。
    猜你喜欢
    • 2011-05-24
    • 2015-06-21
    • 1970-01-01
    • 2014-12-27
    • 2014-09-04
    • 1970-01-01
    • 1970-01-01
    • 2011-06-29
    • 1970-01-01
    相关资源
    最近更新 更多