我有一个非常大的表,有几百万行:
ID (primary)
countrycode
status
flag_cc
我尝试了以下sql语句,但速度很慢:
SELECT id, countrycode, status, flag_cc FROM table WHERE ID>=200000 AND countrycode=3 AND status=1 AND flag_cc=0
所以我认为添加索引以加强查询是个好主意:
ADD INDEX myindex(id, countrycode, status, flag_cc)
然后我问:
EXPLAIN SELECT id, countrycode, status, flag_cc FROM table WHERE ID>=200000 AND countrycode=3 AND status=1 AND flag_cc=0
但是mysql想要使用主键而不是我的键。所以我使用FORCE INDEX并将主键与我的键进行比较。可悲的是,主键更快。
怎么可能?即使主键太慢,是否有可能优化该查询?
答案 0 :(得分:3)
你的问题基本上是“什么是好的指数?”。您可能需要考虑在MySQL文档中阅读它们,在此处使用stackoverflow并使用任何搜索引擎。
在大型百科全书中考虑像索引一样的索引。定义了很多主题,因此索引可以帮助您更快地找到所需内容。
但索引应该是什么?类别(科学,娱乐,人,...)?然后,当您找到该类别时,仍然有大量文章属于每个类别。假设共有1万篇文章,其中1000篇属于科学类别。如果您正在寻找科学的东西,那么仍然会留下1000篇文章来查看您的确切文章。在数据库术语中,这个索引没有良好的基数:如果你没有别的东西,但是不够具体到真正加速的话,这很好。通过开头的字母(字母表中的26个字母,因此使用索引来划分要查找的文章数量大约为26,这也不是非常具体),同样适用于索引。
在数据库中,这意味着主键是一个非常好的索引字段:该字段的一个值对应于数据中的一个值,因此一旦使用索引查找它,就没有什么可以查看了;你已经找到了具体的记录。
另一方面,真/假标志只会将您的数据划分为最多两组,因此即使在使用索引后仍会留下大量数据。
当然也有例外。例如,具有true / false列的表。通常这是一个糟糕的索引列。但是,您可能知道所有记录中只有0.01%将具有该列的值“true”,并且您的查询将查找真值,而不是假值。在这种情况下,该真/假列是一个很好的索引列。
然后是范围问题:你没有搜索特定的ID,而是搜索它们的整个范围,所以即使ID是唯一的,它仍然会标记索引的整个部分(以及数据)作为'使用索引后仍然可以看到的东西'。因此,虽然它具有良好的基数,但它可能不是用于此特定查询的最佳索引。
另一个问题是,当您不搜索索引的第一列时,MySQL无法查看多列索引。所以一个索引(ID,国家代码,状态,flag_cc)意味着MySQL仍然必须开始使用ID的索引,在你的查询中是一个范围条件,前一段解释了为什么这是坏的。只有在应用了索引的ID部分之后才能从国家代码部分开始,如果MySQL确定甚至还值得付出努力。这可能就是为什么MySQL想要使用你的主键索引,即使你已经给它另一个选项。
在您的表上应用所有这些信息:您的where子句包含所有列,因此构建一个索引,从具有最高基数(最不同的值)的列开始,并且不用作范围where子句(所以不ID
)。如果flag_cc
包含大量不同的值,请使用它。如果status
或countrycode
包含更多不同的值,请使用其中一个。根据您索引的第一列的具体方式,索引单个列可能就足够了。如果没有,请尝试将具有次佳基数的列添加到索引等等。
当然,请记住索引(通常并非总是)会加快查找速度,但会降低更新,插入和删除速度!
所以你看,这不是一个非常简单的问题。还要考虑我所概述的内容只是索引冰山的一角。
来源:
http://webmonkeyuk.wordpress.com/2010/09/27/what-makes-a-good-mysql-index-part-2-cardinality/
https://dev.mysql.com/doc/refman/5.6/en/multiple-column-indexes.html