我有一个包含word,A_,E_,U_等列的表。这些带X_的列是tinyints,其值为字中存在特定字母的次数(以便稍后帮助优化通配符搜索查询)。
总共有252k行。如果我像WHERE那样搜索u_> 0我得到60k行。但是,如果我做那个选择的解释,它说有225k行可以通过,没有索引可能。为什么?列被添加为索引。为什么它没有说要经过60k行,那可能的关键是U _?
列出表上的索引(也很奇怪其他人在A_索引下分组)
相比之下,如果我运行查询:其中id> 250000我得到2983个结果,如果我解释了那个选择它说有2982行和密钥用于主要。
顺便说一句,如果我按U_分组得到这个:(但可能并不重要,因为我已经说过查询返回60k结果)
修改
如果我创建了列U(varchar(1))并进行更新U =' U'其中U_> 0,那么如果我选择WHERE U =' U'我也得到了6万行(很明显),但是如果我解释的话我会得到这个:
仍然不太好(行120k不是60k)但至少比先前情况下的行225k好。虽然这个解决方案比第一个解决方案有点小,但可能更有效率。
答案 0 :(得分:2)
我的经验是,MySQL选择进行表扫描,即使您正在搜索的列上有索引,如果您的查询将选择超过表中大约25%的行。
原因是在InnoDB中使用二级索引比使用主索引要多一些。
u_
上的索引。u_
中该值的行的相应主键值。实际上至少双要通过辅助键查找的工作。如果您最终匹配表的少数几行,并且肯定存在二级索引对您的查询非常重要的情况,则这不是问题。因此,不要不愿意使用二级索引。
但是如果你的查询匹配太多的行,并且它成为表的一个重要部分,那么从头到尾扫描表就不那么重要了。
通过类比,为什么书后面的索引不包含单词""?因为该条目自然会列出书中的每一页,所以您可以参考索引然后使用它来引导您进入本书主要部分的每个页面。 。你最好只读这本书。
MySQL没有任何正式记录的阈值来选择索引搜索的表扫描。 25%的数字只是我的经验(实际上有时它似乎接近21%,但我不能很好地了解代码,以确切了解阈值的计算方式)。
我已经看到匹配的行的比例非常接近实现中的任何阈值的情况,并且优化器的行为实际上可以从一个查询翻转到下一个查询,导致性能变化很大。
如果这种情况适用于您,您可以使用index hint使MySQL的优化器假装表扫描成本过高,并且它应该更喜欢表扫描的索引。这是通过FORCE INDEX
提示完成的。
SELECT * FROM words FORCE INDEX(U_) WHERE U_ > 0
我仍然试图保守地使用索引提示。除极少数情况外,它们是不必要的,并且使用索引提示意味着您的查询必须包含索引名称。这使得在不破坏应用程序代码的情况下很难更改索引。
答案 1 :(得分:1)
您正在询问后端查询优化器。特别是你要问:"它如何选择访问路径?为什么要在这里索引但是表格可以在那里?"
让我们考虑一下优化器。什么是优化?经过的时间,在期待中。它具有连续读取和随机读取所用时间以及查询选择性(即查询返回的预期行数)的模型。从几个备选访问路径中,它选择看起来需要最少经过时间的路径。
您的id > 250000
查询包含了一些内容:
id
是主键,因此在导航到btree中的正确位置时,所有列都可立即使用这导致优化器计算索引访问路径的预期已用时间,远小于表扫描的预期时间。
另一方面,您的u_ > 0
查询的选择性非常差,将近四分之一的行拖到结果集中。此外,索引不是*
将所有列值复制到结果集中的要求的覆盖索引。因此优化器预测它必须读取四分之一的索引块,然后基本上读取它们指向的所有数据行块。因此,与tablescan相比,我们必须从磁盘读取更多块,和它们将是随机读取而不是顺序读取。这两个人都反对使用索引,因此选择tablescan是因为它最便宜。此外,请记住,通常多行将适合单个磁盘块或单个读取请求。如果它总是选择索引访问路径,我们会称它为悲观者,即使在索引磁盘I / O需要更长时间的情况下也是如此。
当您的查询具有良好的选择性时,在单个列上使用索引,返回远小于关系行的1%。当您的查询选择性较差且您愿意进行空格与时间的权衡时,请使用covering index。