数字列上的MySQL索引使查询速度变慢

时间:2010-12-15 01:07:33

标签: mysql optimization indexing

我有一个相当大的表(~1.7M行)的优化问题。

选择行时会使用两列,我们称之为colA和colB。它们都是'double'类型(5位小数),范围从:

colA:-90~90 colB:-180~180

没有索引,任何形式的查询:

SELECT * FROM table where colA BETWEEEN a and b AND colB BETWEEN c and d
无论(a,b)和(c,d)的范围如何,

运行大约相同的时间(~1秒)(因为MySQL必须检查每一行)。

如果我向colA和colB添加索引,会发生两件事:查询(a,b)& (c,d)范围很小,例如:

SELECT * FROM table where colA BETWEEEN -4 and 4 AND colB BETWEEN 3 and 7

跑得很快(约1/10秒)。但是,执行时间随查询值之间的范围而增加。例如:

 SELECT * FROM table where colA BETWEEEN -80 and 80 AND colB BETWEEN -150 and 150

需要大约一分钟才能执行。

我知道B-tree如何用于字符串,但是当数据是数字并且使用范围进行查询时,我不确定机制。

如果有人可以建议如何优化此查询,我将不胜感激。一种想法是使用小范围的索引并告诉MySQL不要将它用于较大的范围,但是我找不到允许它的命令。

由于

编辑:解释

有些事我愚蠢地忘了提及。结果按rand()排序 - 我知道这是多么低效,但我没有办法从表中随机获取有限数量的行。

添加rand()不会影响没有索引时的执行时间,但会大大增加时间。

EDIT2:这是使用复合索引。

小范围:

“从表中解释select *,其中colA介于35和38之间,colB介于-10和5之间,ORDER BY RAND()LIMIT 20”

9783行

没有指数(快)

+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows    | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
|  1 | SIMPLE      | table | ALL  | NULL          | NULL | NULL    | NULL | 1673784 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+

WITH INDEX(非常快)

+----+-------------+-------+-------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+-------+-------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | table | range | test          | test | 18      | NULL | 136222 | Using where |
+----+-------------+-------+-------+---------------+------+---------+------+--------+-------------+



大范围:

“从表中解释select *,其中colA介于-80和80之间,colB介于-150和150之间,ORDER BY RAND()LIMIT 20;”

1631862行

没有指数(快)

+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows    | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
|  1 | SIMPLE      | table | ALL  | NULL          | NULL | NULL    | NULL | 1673784 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+

WITH INDEX(非常慢:> 60秒)

+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows    | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
|  1 | SIMPLE      | table | ALL  | test          | NULL | NULL    | NULL | 1673784 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+

EDIT3:

总结:(所有查询仅限于返回20行)

带有索引的rand()的大范围: 45秒
没有rand()的大范围,索引:0.003秒

范围大,没有指数:1秒
没有兰特的大范围,没有指数:0.003秒

异常是:“大范围与rand()与索引,45秒”。

3 个答案:

答案 0 :(得分:5)

  

我知道B-tree如何用于字符串,但是当数据是数字并且使用范围进行查询时,我不确定机制。

它们对数字的工作方式与对字符串的工作方式相同。

  

如果没有索引,查询大约需要运行大约相同的时间(~1秒),无论(a,b)和(c,d)的范围如何

全表扫描的运行时间与WHERE条件的内容没有显着差异。索引访问路径所花费的时间与返回的行数成比例。如果查询选择了表的重要部分,则使用索引总是比不使用索引慢。

索引访问路径仅在索引选择性足够时才有效,即检索的行数很少(有人说最多10%)。执行时间将与返回的行数大致成比例,并且最终可能比完整的表扫描速度慢。

  

一种想法是使用小范围的索引并告诉MySQL不要将它用于较大的范围,但是我找不到允许它的命令。

查询优化器必须使用统计信息和启发式方法来确定是否应使用索引。也许您需要使用OPTIMIZE TABLE更新这些统计信息。如果仍然无法做出正确的决定,您可以使用hints帮助它。

SELECT * FROM table 
   IGNORE INDEX (the_index)
   where colA BETWEEEN -80 and 80 AND colB BETWEEN -150 and 150

其他选项可能是删除索引(如果你从未看到它的任何好处,一个恒定的一秒响应时间可能就足够了),或者在两个列上尝试复合索引(也只有当记录产生的记录数量时)查询很小。)


现在您提到LIMIT 20,它开始变得更有意义了:

  

rand()的大范围,索引:45秒

NESTED LOOP,结果很多+ SORT

从索引中获取所有记录(在范围内),从表中逐个获取它们,然后排序,然后限制为20

  

没有rand()的大范围,索引:0.003秒

NESTED LOOP中止了20条记录

从索引中获取20条记录,从表中逐一获取它们并返回它们。没有排序,实际上没有大范围。

  

rand的大范围,没有指数:1秒

FULL TABLE SCAN + SORT

阅读整个表格,保留范围内的内容,然后排序,然后限制为20

  

没有兰特的大范围,没有指数:0.003秒

FULL TABLE SCAN,中止了20条记录

开始阅读表格,保持范围内的内容,当你有20并且返回时停止。

答案 1 :(得分:0)

包含许多重复项的索引是一种浪费。

确保您的索引使用这两个字段;

create index idx_faster on tbl_mytbl (colA,colB)

对于colB,您可以添加另一个,

create index idx_colb on tbl_mytbl (colB)

的问候, /吨

答案 2 :(得分:0)

上次查询的时间不应超过第一次。 MySQL可能没有更新索引,请参阅OPTIMIZE TABLE

此外,您还可以使用EXPLAINEXPLAIN ANALYZE查看计划查询的方式。

最后,您可以使用IGNORE INDEX (idx_name)

强制禁用索引