Mysql查询in语句都走索引吗
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mysql查询in语句都走索引吗相关的知识,希望对你有一定的参考价值。
参考技术A CREATE TABLE `test_a` (id int PRIMARY KEY AUTO_INCREMENT,
`id_a` varchar(20) ,
`name` varchar(20) ,
`sort` int(11) DEFAULT NULL,
`sort1` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
自增主键,mysql会自动创建唯一索引
表里记录4条
EXPLAIN SELECT * from test_a WHERE id in(1,2,3)
走索引在指定范围内进行查询 range
EXPLAIN SELECT * from test_a WHERE id in(1,2,3,4)
全表扫描,不走索引,表里有4条数据,查询也是4条,mysql判定全表扫描
mysql会根据表里的总条数和查询条数判定是否应该走索引,自己指定force index(索引名字)
MySQL 对多个 OR 使用索引,但对 IN 没有索引,而且速度慢得多
【中文标题】MySQL 对多个 OR 使用索引,但对 IN 没有索引,而且速度慢得多【英文标题】:MySQL using Indexes for multiple ORs, but no index for IN and so much slower 【发布时间】:2019-01-03 13:44:55 【问题描述】:我一直忙于更改一些 SQL 查询,以使它们在人眼看来更具可读性,我还被告知它们可能会快 5-10%。
之前的 SQL 语句如下所示。
SELECT * FROM teams WHERE Team1='Joe Bloggs' OR Team2='Joe Bloggs' OR Team3='Joe Bloggs'
我改成
SELECT * FROM team WHERE 'Joe Bloggs' IN (Team1,Team2,Team3)
新查询大约慢了 10 倍,在检查了可能是什么原因后,我发现它没有使用任何索引,即使我尝试强制使用索引,它仍然不会使用它。
该表大约有 120,000 行,我无法更改表格式,因为我无权访问的其他应用程序使用它。 Team1,Team2,Team3 列都是 VARCHAR(45)
谁能解释为什么索引用于原始查询而不是新查询?我已经阅读了大量页面,但找不到答案,我已经读到 mysql 可能确定不使用索引的速度更快,但是这里不应该是这种情况,因为 IN 查询几乎慢了 10 倍。
多个 ORs SELECT(运行 1000 次,没有缓存)- 12.863906860352 已过 IN SELECT(在没有缓存的情况下运行 1000 次)- 122.73787903786 已过
感谢您的宝贵时间。
【问题讨论】:
为两个版本提供EXPLAIN SELECT ...
。特别是,我正在寻找“索引合并联合”。
【参考方案1】:
在查询中:
SELECT * FROM teams WHERE 'Joe Bloggs' IN (Team1,Team2,Team3)
您正在将一堆列与字符串文字进行比较(查找)。优化器通常会使用搜索目标上的索引(在本例中为 Joe Bloggs
)来查找 IN
子句中的值。但是,它不能在字符串文字上放置索引。所以,这里的一切都颠倒过来了,这就是索引不起作用的原因。
另一方面,在您的第一个查询中:
SELECT * FROM teams WHERE Team1='Joe Bloggs' OR Team2='Joe Bloggs' OR Team3='Joe Bloggs'
MySQL 会抓取字符串文字,然后使用 B-tree 索引在各个列中查找它们。这与您预期和看到的一样。
【讨论】:
。 .我不认为 MySQL 使用OR
的索引,尽管更新的版本可能已经实现了这种优化。
@GordonLinoff 但是如何解释 OP 的观察结果?
OR 查询肯定是在使用索引,它要快得多,并且使用 EXPLAIN 表明它只查询少量行和它正在使用的索引,而 IN 语句正在查询所有表中的行并且不使用索引。
蒂姆,根据您的经验,我应该坚持使用多个 OR,因为到目前为止我的基准标记似乎表明在我的用例中这是最快的,或者它们仍然是一种更简单的编写方式查询,同时仍在使用索引?
您对原始查询的反感是什么,它对您有用,除了它的视觉部分?【参考方案2】:
你有一个“倒置的 IN”;优化器只会使用column in (value1, value2, value3)
的索引。
但是,如果您在 3 列中的每一列上都有单独的索引,那么还有另一种方法可以产生比您的任何尝试都好得多的性能:
SELECT * FROM teams WHERE Team1='Joe Bloggs'
UNION
SELECT * FROM teams WHERE Team2='Joe Bloggs'
UNION
SELECT * FROM teams WHERE Team3='Joe Bloggs'
该表将被查询 3 次,但每次都会使用一个索引。
如果您确定不会有任何欺骗,或者您不介意欺骗,请将 UNION
更改为 UNION ALL
以进一步加快速度(UNION
有额外的开销或重复数据删除)。
【讨论】:
感谢您的回答,我从来不知道“inverted IN”语句是什么,我将查询更改为与您在上面写的完全一样,但不幸的是,在运行基准测试大约 10 次以获得良好的平均值后,它的速度几乎是原始 OR 语句的两倍。 1000 次迭代需要 28.898875951767 秒。它也打败了我所针对的主要目标,即可读性,它对我和其他人来说更容易理解多个 OR,但也许这只是因为我们习惯于长时间查看这些语句。谢谢你的回答:) @twingo 我只是编造了“inverted IN”这个名字来帮助描述这种模式(不过我会从现在开始继续使用它,因为它听起来很酷)。很遗憾听到重写对您不利。您确定定义了 3 个索引 - 每列一个索引吗?如果是这样,请尝试运行analyze teams
。但是,可能是 MySQL 的优化器做得很好,而您原来的 OR 查询就是要走的路。
@Twingo - 计时时,运行两次并进行第二次计时。第一个可能涉及 I/O;由于缓存数据,后续运行可能彼此相同。
@Twingo - 通过将OR
写入多行并对齐文本,OR
将与IN
一样清晰易读。【参考方案3】:
计划 A:使用 FULLTEXT (team1, team2, team3)
和 MATCH(team1, team2, team3) AGAINST ('+Joe +Briggs' IN BOOLEAN MODE)
。使用这种方法有很多注意事项,但是,如果它适用于您的情况,它会非常快。
B 计划:尽管“无法更改表格格式”,但您可以使用 VIEW 玩一些游戏,以避免跨列展开数组(团队)。
【讨论】:
【参考方案4】:我不知道为什么性能会有所不同——在这两种情况下似乎都不会使用索引。
你可以这样写查询:
SELECT t.*
FROM teams t
WHERE Team1 = 'Joe Bloggs'
UNION ALL
SELECT t.*
FROM teams t
WHERE Team2 = 'Joe Bloggs' AND Team1 <> 'Joe Bloggs'
UNION ALL
SELECT t.*
FROM teams t
WHERE Team3 = 'Joe Bloggs'
AND Team2 <> 'Joe Bloggs'
AND Team1 <> 'Joe Bloggs';
这可以利用(Team1)
、(Team2, Team1)
和(Team3, Team2, Team1)
上的索引。
【讨论】:
正如我在上面的评论中提到的,索引最肯定与多个 OR 查询一起使用,在语句显示它使用索引之前使用 EXPLAIN 并且它显示它只查询少量 ROWS,vs执行全表扫描且不使用索引的 IN 语句。我真的很感谢你抽出时间来写你的答案,不幸的是它比使用多个 OR 运行得慢,而且它使人类阅读查询比只使用 OR 几次更难。虽然我完全欣赏你的方式,但也许是正确的方式。 是的,MySQL 可以使用索引合并优化来为or
等条件使用索引,请参阅dev.mysql.com/doc/refman/5.5/en/index-merge-optimization.html 正如您所看到的,该功能至少从 v5.5 开始可用。
@Shadow 。 . .令人惊讶的是,合并扫描不会用于 in
列列表。
@GordonLinoff - MySQL 的设计和开发历史充斥着仅仅足以说实现了一个特性。 ALTER
总是重建表时非常简单(实现)。现在 5.6、5.7 和 8.0 进行了多次尝试,以挤出所有可能的优化。我一直在关注EXPLAINs
,因为索引合并被实施(在 4.1 中?)它几乎从未被使用过,即使对于看起来很可能的查询也是如此。它可能只针对IN
,而不是JOIN
,当然也不是IN
。我会说“倒置 IN”非常罕见。
@GordonLinoff - 功能存在但未优化的另一种情况:WHERE (a, b) > (123, 345)
-- INDEX(a,b)
没有帮助。解决方法很丑陋(尽管已优化)。超过 3 列更难看。以上是关于Mysql查询in语句都走索引吗的主要内容,如果未能解决你的问题,请参考以下文章