你如何强制MySQL中的查询使用索引?

时间:2013-07-02 21:14:04

标签: mysql wordpress indexing explain

我试图通过向慢查询日志中出现的查询添加索引来提高锤击wordpress DB的性能。

在MS SQL中,您可以使用查询提示强制查询使用索引,但如果正确覆盖列等,通常很容易获得查询以使用索引。

我的查询很慢地出现在慢查询日志中

SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID 
FROM wp_posts  
WHERE 1=1  
  AND wp_posts.post_type = 'post' 
  AND (wp_posts.post_status = 'publish')  
ORDER BY wp_posts.post_date DESC 
  LIMIT 18310, 5;

我在wp_posts post_date, post_status, post_type and post_id上创建了一个覆盖唯一索引并重新启动MySQL但是当我运行解释时使用的索引是

status_password_id

并且在可能的键中我的新索引甚至没有出现,尽管它是一个覆盖索引,例如我只是得到

type_status_date,status_password_id

因此,如果MySQL有一个,那么所使用的索引或“optimiser”的可能选择都不会考虑我的post_date作为第一列的索引。我本来以为一个查询基本上是做一个TOP并按日期用

排序
ORDER BY wp_posts.post_date DESC LIMIT 18310, 5;

是否希望使用按日期排序的索引来获取速度,尤其是具有满足查询所需的所有其他字段的索引?

MySQL是否有查询提示强制索引用于速度/性能测试,或者我还需要做些什么来查看为什么忽略这个索引。

如果Navicat有像MS SQL这样的可视化查询执行计划,我会很高兴,但看起来EXPLAIN是它提供的最好的。

任何有关如何强制使用索引或找出被忽略的原因的任何提示都会非常有用!

由于

4 个答案:

答案 0 :(得分:9)

  

MySQL是否有查询提示强制索引用于速度/性能测试,或者我还需要做些什么来查看为什么忽略这个索引。

The documentation详细回答了这个问题:

  

通过指定USE INDEX (index_list) ,您可以告诉MySQL使用   只有一个命名索引可以在表中查找行。该   替代语法IGNORE INDEX (index_list) 可用于说明   MySQL不使用某些特定的索引或索引。这些提示是   如果EXPLAIN表明MySQL使用了错误的索引,则非常有用   从可能的索引列表中。

     

您还可以使用FORCE INDEX,其行为类似于USE INDEX (index_list)   但另外还假设表扫描非常   昂贵。换句话说,只有在没有的情况下才使用表扫描   使用给定索引之一来查找表中的行的方法。

     

每个提示都需要索引的名称,而不是列的名称。该   PRIMARY KEY的名称为PRIMARY。查看a的索引名称   表,使用SHOW INDEX

如果USE INDEX不起作用,请尝试使用IGNORE INDEX查看优化程序的第二个选择(或第三个,等等)。

语法的一个简单示例是:

SELECT * FROM t1 USE INDEX (i1) IGNORE INDEX (i2) WHERE ...

链接文档中还有更多来自哪里。我已链接到5.0版本的页面,但您可以使用左侧边栏轻松导航到相应的版本;从版本5.1开始,可以使用一些其他语法选项。

答案 1 :(得分:6)

MySQL 5.6支持EXPLAIN的新格式,MySQL Workbench GUI可以以更具吸引力的方式显示。但是如果你坚持使用MySQL 5.5或更早版本,这对你没有帮助。

MySQL确实有@AirThomas提到的提示,但你应该谨慎使用它们。在您展示的简单查询中,如果您拥有正确的索引,则永远不必使用索引提示。使用索引提示意味着您的应用程序中有硬编码索引名称,因此如果添加或删除索引,则必须更新代码。

在您的查询中,(post_date, post_status, post_type, post_id)上的索引无济于事。

您希望索引中最左侧的列用于行限制。所以先放post_status, post_type。如果选择性较高的列是第一个,那就最好也就是说,如果post_type = 'post'匹配表的3%,post_status = 'publish'匹配表的1%,则将post_status放在post_type之前。

由于您使用=用于两个条件和AND运算符,您知道所有匹配的行基本上都与这两列相关联。因此,如果您使用post_date作为索引中的第三列,则优化器知道它可以按照它们存储在索引中的顺序获取行,并且它可以跳过为ORDER BY执行任何其他工作。如果“使用filesort”从EXPLAIN输出中消失,你可以看到这个工作。

所以你的索引应该是:

ALTER TABLE wp_posts ADD INDEX (post_status, post_type, post_date);

您也可以欣赏我的演示文稿How to Design Indexes, Really

在这种情况下,您不需要为索引添加ID,因为InnoDB索引会自动包含主键列。

LIMIT 18310, 5一定是代价高昂的。 MySQL必须在服务器端生成整个结果集,最多18315行,只丢弃其中的大多数。无论如何,世界上谁还需要跳到3662 nd 页面?!

SQL_CALC_FOUND_ROWS是一个主要的性能杀手,当你有大量的结果集进行分页时,因为MySQL必须生成整个结果集,您请求的页面之前和之后。除非你确实需要FOUND_ROWS(),否则最好除去那个查询修饰符,即使你确实需要行数,它有时可以更快地运行两个查询,一个查询SELECT COUNT(*)。 /> (*测试两种方法以确保。)

以下是有关优化LIMIT的更多提示:

答案 2 :(得分:1)

尝试将索引定义的顺序更改为

post_type, post_status, post_date, post_id

post_date desc, post_type, post_status,  post_id

答案 3 :(得分:0)

只是为了让你知道我在不同的电脑上,所以我的用户名已经改变,但我确实写了原来的问题。

我认为非常有用的是一个转换指南,帮助MS SQL背景的人转向MySQL,因为它似乎在索引调优方面存在一些差异,我没有意识到特别是不同的存储引擎会自动添加主键,以及如何处理缺乏有助于性能调整的工具。

我习惯于创建主聚集索引,主键和其他唯一约束和索引,然后使用包含键的非聚簇索引,一些覆盖索引等。

然后,我将运行一个定时作业,将缺少的索引DMV报告记录到表中,以防止数据在任何重新启动期间丢失。然后,我可以运行报告来检查SQL优化器认为“应该”使用的索引或者“不”使用的索引。然后,我可以使用这些信息,如果使用缺失的索引,可以使用错误命中和潜在效率百分比,作为帮助微调性能索引的指南。

据我所知,MySQL没有类似于DMV的MsSQL有什么?

从永恒之前构建到MS SQL Studio中的漂亮的图形执行计划有助于进行大量调整,而标准MySQL解释的沼泽标准相比较差。我会研究你提到的那个工具,虽然运行select @@版本会返回5.0.51a-24 + lenny5-log,所以我怀疑它会对我有所帮助。

关于帖子的一些事情:

  1. 目标是有一个覆盖索引,因此不需要书签查找(如果你在MySQL中调用它们),并且数据可以直接来自索引。

  2. 几乎所有帖子都是“已发布”(99.99%),而post_type几乎都是“帖子”(99.99%),只有很小比例的“页面”。这两列中没有选择性,它们位于封面的索引中。我已经关闭了自动草稿以防止修改等等,并且草稿的数量非常少。

  3. 因此我认为将post_date作为索引中的第一个键会有更多的帮助,因为LIMIT(正如你所说的那样昂贵,而且我无法控制Wordpress的代码)因此肯定是ORDER BY和LIMIT(基本上是TOP)将是查询中最昂贵和最具选择性的部分,与其他键(根本不是选择性的)相比,它对索引的使用更多。这就是我把它放在第一位的原因。

  4. 我使用的是Wordpress,表格是wp_posts,其存储引擎是MyISAM,我认为由于要求全文搜索,我无法更改。

  5. 正如我对别人说的那样,我已经有一个订单 post_type,post_status和post_date ,但是EXPLAIN只在可能的键中显示它,然后忽略它以使用基于索引的围绕这些列: post_status,密码和ID

  6. 由于密码未在查询中使用,而post_status完全没有选择性(因为我的所有post_types都是“post”)我对MySQL “聪明”优化器的原因感到遗憾认为应该在提供的OR或者我自己的上面选择这个索引吗?

  7. 所以我仍然坚持,似乎没有任何建议可行。

    我已尝试多次更改订单,即使我只有20k行,每次需要半小时或更长时间!我不知道这在MySQL中是否正常,但在MSSQL中,在数百万行的表上添加/删除索引需要几分钟。

    因此到目前为止我没有任何工作,我想知道(为什么?),显然是关于查询提示,看看它是否有任何好处。

    我在重新编制索引后重新启动了数据库(甚至重新启动了网络服务器)。

    感谢您的帮助。