为 mySQL 中的特定查询优化索引

Optimize Indexes for Particular Query in mySQL

我有一个相当简单的查询,大约需要 14 秒才能完成,我想加快它的速度。我想我有正确的索引,但我不确定...

这里是查询

SELECT *
FROM opportunities
WHERE cid = 7785
  AND STATUS != 4
  AND otype != 200
  AND links > 0
  AND ontopic != 'F'
ORDER BY links DESC
LIMIT 0, 100;

这是table架构

CREATE TABLE `opportunities` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `cid` int(11) NOT NULL,
  `url` varchar(900) CHARACTER SET utf8 NOT NULL,
  `status` tinyint(4) NOT NULL,
  `links` int(11) NOT NULL,
  `otype` int(11) NOT NULL,
  `reserved` tinyint(4) NOT NULL,
  `ontopic` varchar(3) CHARACTER SET utf8 NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `cid` (`cid`,`url`),
  KEY `cid1` (`cid`),
  KEY `url` (`url`),
  KEY `otype` (`otype`),
  KEY `reserved` (`reserved`),
  KEY `ontopic` (`ontopic`),
  KEY `status` (`status`),
  KEY `links` (`links`),
  KEY `ontopic_links` (`ontopic`,`links`),
  KEY `cid_status_otype_links_ontopic` (`cid`,`status`,`otype`,`links`,`ontopic`)
) ENGINE=InnoDB AUTO_INCREMENT=13022832 DEFAULT CHARSET=latin1

这里是 EXPLAIN 命令的结果

id: 1
select_type: Simple
table: opportunities
partitions: null
type: range
possible_keys: cid,cid1,otype,ontopic,status,links,ontopic_links,cid_status_otype_links_ontopic
key: links
keylen: 4
ref: null
rows: 1531552
filtered: 0.33
Extra: Using index condition; Using where

想法/问题

我读得正确吗,它正在使用 "links" 键进行查询?为什么它不使用更完整的索引,例如涵盖我查询的所有条件的 cid_status_otype_links_ontopic?

提前致谢!

根据要求

删除 LIMIT 0,100 后,有 30,961 个结果与查询匹配。有趣的是,"count()"命令returns几乎是瞬间完成的。

  • 您必须使用 5 列索引遍历所有行,然后对结果进行排序并提供 100 行。

  • 唯一可能有用的索引是 INDEX(cid, links)。这是因为 cid 是唯一用 = 测试的列,然后 links 可能 ORDER BYLIMIT。仍然存在 != 测试需要过滤大量行的风险。

  • statusotype 是多值的吗?如果其中一个只有 2 个值,那么将 != 转换为 = 并将其添加到索引中将是有益的。

  • 您真的需要所有列 (SELECT *) 吗?如果不是,并且如果你不需要任何大列(url),那么你可以使用'covering'索引。

More on writing indexes .

使用不等式比较是一件有趣的事情,它们算作 范围 条件。

也就是说,相等匹配一个值,但除相等以外的任何值(!=><INBETWEEN)。

通过匹配多个值,这意味着只有在范围条件中使用的索引中的第一列将被优化。您可能认为您的索引 cid_status_otype_links_ontopic 包含查询条件中提到的所有列,但只会使用前两个。第一个是因为您对 cid 进行了相等比较。第二个是因为下一列用于不等式比较,然后它停止使用索引中的列。*

证据:如果您可以强制使用该索引,您应该会看到 EXPLAIN 结果的 keylen 字段仅显示 5,这是 cid 的大小(4 个字节)+ status(1 个字节)。

MySQL 优化器显然已经预测到使用您的 links 索引会更有利,因为这允许它按索引顺序访问行,这与排序相同您使用 ORDER BY 请求的订单。

证据:你没有在你的 EXPLAIN 笔记中看到 "Using filesort"。

这真的比使用其他索引之一更好吗?也许,也许不是。优化器的预测并不总是完美的。

您可以使用 index hint 覆盖优化器的选择:

SELECT * FROM opportunities USE INDEX (cid_status_otype_links_ontopic) WHERE ...

尝试一下,执行该查询的 EXPLAIN 并将其与您的其他 EXPLAIN 进行比较。然后执行两个查询,看看哪个更快。

(* 实际上,我必须添加一个关于索引列用法的脚注。MySQL 5.6 及更高版本可以比仅两列做得更好,当您看到注释 "Using Index Condition" 在 EXPLAIN 中。但它并不完全相同。您可以在此处阅读更多相关信息:https://dev.mysql.com/doc/refman/5.6/en/index-condition-pushdown-optimization.html)