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.
答案 0 :(得分:2)
如果我正确理解了您的混淆,那么您在一对列中有一个start
和end
值,例如日期时间或IP地址?你想看看你的给定日期时间/ ip是否在给定范围内?
嗯,没有办法在这样的表上一般地优化这样的查询。优化器不知道给定值是否可以在多个范围内。或者换句话说,范围是否是不相交的。
因此,优化器最多会使用以start
或end
开头的索引并扫描一半的表。效率不高。
范围是否不重叠? 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.low
,t.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
上的索引的有效范围扫描。在这种情况下,我上面提到的覆盖索引肯定会加速这个查询。
答案 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;到表的开头,结果很快就产生了;另一方面,如果记录接近表的末尾,则需要几秒钟才能完成。)
[顺便说一句,我假设您理解我的意思是开始和结束表格
同样,我确信人们可能不喜欢这个,但它确实适合我。
谢谢大家的帮助!!