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) &gt; (123, 345) -- INDEX(a,b) 没有帮助。解决方法很丑陋(尽管已优化)。超过 3 列更难看。

以上是关于Mysql查询in语句都走索引吗的主要内容,如果未能解决你的问题,请参考以下文章

mysql in 会使用索引吗?

mysql百万数据查询 用啥代替in,该如何处理

Mysql - like 语句会不会走索引

mysql主键索引(id=和id in)查询慢问题

MySQL 查询优化:IN() 与 OR

mysql in 会使用索引吗