为 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 BY
和 LIMIT
。仍然存在 !=
测试需要过滤大量行的风险。
status
和 otype
是多值的吗?如果其中一个只有 2 个值,那么将 !=
转换为 =
并将其添加到索引中将是有益的。
您真的需要所有列 (SELECT *
) 吗?如果不是,并且如果你不需要任何大列(url
),那么你可以使用'covering'索引。
使用不等式比较是一件有趣的事情,它们算作 范围 条件。
也就是说,相等匹配一个值,但除相等以外的任何值(!=
、>
、<
、IN
、BETWEEN
)。
通过匹配多个值,这意味着只有在范围条件中使用的索引中的第一列将被优化。您可能认为您的索引 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)
我有一个相当简单的查询,大约需要 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 BY
和LIMIT
。仍然存在!=
测试需要过滤大量行的风险。status
和otype
是多值的吗?如果其中一个只有 2 个值,那么将!=
转换为=
并将其添加到索引中将是有益的。您真的需要所有列 (
SELECT *
) 吗?如果不是,并且如果你不需要任何大列(url
),那么你可以使用'covering'索引。
使用不等式比较是一件有趣的事情,它们算作 范围 条件。
也就是说,相等匹配一个值,但除相等以外的任何值(!=
、>
、<
、IN
、BETWEEN
)。
通过匹配多个值,这意味着只有在范围条件中使用的索引中的第一列将被优化。您可能认为您的索引 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)