MySQL - Poor performance in a select from a simple table

时间:2016-08-13 13:32:52

标签: mysql database-performance

I have a very simple table with three columns:

- A BigINT,
- Another BigINT,
- A string.

The first two columns are defined as INDEX and there are no repetitions. Moreover, both columns have values in a growing order.

The table has nearly 400K records.

I need to select the string when a value is within those of column 1 and two, in order words:

SELECT MyString 
  FROM MyTable
 WHERE Col_1       <= Test_Value 
   AND Test_Value  <= Col_2       ;

The result may be either a NOT FOUND or a single value.

The query takes nearly a whole second while, intuitively (imagining a binary search throughout an array), it should take just a small fraction of a second.

I checked the index type and it is BTREE for both columns (1 and 2).

Any idea how to improve performance?

Thanks in advance.

EDIT: The explain reads:

Select type: Simple, Type: Range, Possible Keys: PRIMARY Key: Primary, Key Length: 8, Rows: 441, Filtered: 33.33, Extra: Using where.

3 个答案:

答案 0 :(得分:2)

如果我正确理解了您的混淆,那么您在一对列中有一个startend值,例如日期时间或IP地址?你想看看你的给定日期时间/ ip是否在给定范围内?

嗯,没有办法在这样的表上一般地优化这样的查询。优化器不知道给定值是否可以在多个范围内。或者换句话说,范围是否是不相交的。

因此,优化器最多会使用以startend开头的索引并扫描一半的表。效率不高。

范围是否不重叠? IP Addresses

你对结果怎么说?也许像这样的kludge将起作用:SELECT ... WHERE Col_1 <= Test_Value ORDER BY Col_1 DESC LIMIT 1

答案 1 :(得分:1)

使用较短标识符重写的查询是

SELECT s FROM t WHERE t.low <= v AND v <= t.high

使用索引来满足此查询将如下所示:首先,我们必须在表或索引中搜索与这些条件中的第一个匹配的所有行

  t.low <= v

我们可以将其视为BTREE指数的半扫描。它从头开始,到达v时停止。

它需要在另一个索引中进行另一次半扫描才能满足v <= t.high。然后,它需要合并两个结果集以标识符合两个条件的行。问题是,要合并的两个结果集很大,并且它们几乎完全不重叠。

因此,查询计划程序可能只需选择全表扫描即可满足您的条件。在MySQL的情况下尤其如此,其中查询规划器并不擅长使用多个索引。

您可以(也可以不)使用原始列名(low, high, s)使用(Col_1, Col_2, MyString)上的复合索引加快此确切查询的速度。这称为covering index,允许MySQL完全从索引中满足查询。它有时有助于提高性能(如果表的确切定义可用,则更容易猜测这是否有用;覆盖索引的效率取决于其他索引,主键,列大小等等。但是您已经选择了对该信息的最低披露。)

这会有什么帮助?重新思考你的算法可以为你带来很多好处。您似乎正在尝试检索测试点v位于[t.lowt.high]范围内的行。您的应用程序是否提供范围宽度的先验限制?也就是说,是否已知t.high - t.low的最大值?如果是,请让我们调用该值maxrange。然后你可以像这样重写你的查询:

 SELECT s
   FROM t 
  WHERE t.low BETWEEN v-maxrange AND v
    AND t.low <= v AND v <= t.high

maxrange可用时,我们可以添加col BETWEEN const1 AND const2子句。这转变为low上的索引的有效范围扫描。在这种情况下,我上面提到的覆盖索引肯定会加速这个查询。

阅读本文。 http://use-the-index-luke.com/

答案 2 :(得分:0)

嗯......我为我找到了一个合适的解决方案(不确定你的人会喜欢它,但如上所述,它对我有用)。

我只是将400K记录划分为多个表,并创建了一个用作选择器的简单表:

选择器表保存每个分区的第一列的最小值以及一个简单的索引(即1,2,...)。

然后我使用以下内容来获取应该包含搜索范围的表的索引,如:

SELECT Table_Index 
  FROM tbl_selector 
 WHERE start_range <= Test_Val
 ORDER BY start_range DESC LIMIT 1 ;

这将为我提供我想要选择的表的索引。

然后我在检索到的索引上有一个CASE,用于从执行实际搜索中选择正确的分区表。

(我想更优雅的是使用动态SQL,但稍后会处理它;现在只想测试方法。)

结果是我得到的响应远低于一秒(~0.08)并且无论用于测试的数量是多少都是均匀的。顺便说一下,这与以前的方法并非如此:如果数字是&#34;关闭&#34;到表的开头,结果很快就产生了;另一方面,如果记录接近表的末尾,则需要几秒钟才能完成。)

[顺便说一句,我假设您理解我的意思是开始结束表格

同样,我确信人们可能不喜欢这个,但它确实适合我。

谢谢大家的帮助!!