4

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

在 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我在 on上创建了一个覆盖唯一索引post_date, post_status, post_type and post_id并重新启动了 MySQL,但是当我运行解释时,使用的索引是

status_password_id

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

type_status_date,status_password_id

因此,使用的索引或如果 MySQL 有一个“优化器”的可能选择,甚至都没有考虑我的索引,它的第一列是 post_date。我会想到一个基本上是在做 TOP 并按日期排序的查询

ORDER BY wp_posts.post_date DESC LIMIT 18310, 5;

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

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

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

任何对我如何强制使用索引或弄清楚为什么它被忽略的任何提示的人都会非常有帮助!

谢谢

4

4 回答 4

12

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

该文档详细回答了这个问题:

通过指定USE INDEX(index_list),您可以告诉 MySQL 仅使用一个命名索引来查找表中的行。替代语法IGNORE INDEX(index_list)可用于告诉 MySQL 不要使用某些特定的索引或索引。EXPLAIN如果显示 MySQL 使用了可能索引列表中的错误索引,这些提示很有用。

您也可以使用FORCE INDEX, 它的行为类似于USE INDEX(index_list) 但另外假设表扫描非常昂贵。换句话说,仅当无法使用给定索引之一来查找表中的行时,才使用表扫描。

每个提示都需要索引的名称,而不是列的名称。a 的名字PRIMARY KEYPRIMARY。要查看表的索引名称,请使用SHOW INDEX.

如果USE INDEX不起作用,请尝试使用IGNORE INDEX来查看优化器的第二个选择是什么(或第三个,依此类推)。

一个简单的语法示例是:

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

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

于 2013-07-02T21:46:05.010 回答
6

MySQL 5.6 支持一种新的 EXPLAIN 格式,MySQL Workbench GUI 可以以更吸引人的方式对其进行可视化。但是如果你被困在 MySQL 5.5 或更早版本上,这对你没有帮助。

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

在您的查询中,索引(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 做任何其他工作。如果“使用文件排序”从您的 EXPLAIN 输出中消失,您可以看到这个工作。

所以你的索引可能应该是:

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

您也可能会喜欢我的演示文稿如何设计索引,真的

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

LIMIT 18310, 5必然是代价高昂的。MySQL 必须在服务器端生成整个结果集,最多 18315 行,只丢弃其中的大部分。到底谁需要跳到第3662?!

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

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

于 2013-07-02T22:15:25.137 回答
1

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

post_type, post_status, post_date, post_id

或者

post_date desc, post_type, post_status,  post_id
于 2013-07-02T21:26:36.530 回答
0

只是为了让您知道我在另一台 PC 上,所以我的用户名已更改,但我确实写了原始问题。

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

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

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

据我所知,MySQL 没有任何类似于 DMV 的 MsSQL 的东西吗?

很久以前内置到 MS SQL Studio 中的漂亮图形执行计划有助于进行很多调整,相比之下,沼泽标准 MySQL 解释很差。我会研究你提到的那个工具,尽管运行 select @@version 返回 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、password 和 id

  6. 由于查询中未使用密码并且 post_status 完全没有选择性(因为我所有的 post_types 都是“post”),我不知道为什么 MySQL 的“聪明”优化器认为应该在提供的索引或我自己的索引之上选择这个索引?

所以我仍然被困住,因为似乎没有任何建议有效。

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

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

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

谢谢你的帮助。

于 2013-07-03T11:39:48.053 回答