为什么MYSQL更高的LIMIT偏移会降低查询速度?

时间:2010-12-19 03:01:05

标签: mysql performance sql-order-by limit

简短情景:一张记录超过1600万的表[大小为2GB]。使用ORDER BY * primary_key *

时,SELECT的LIMIT偏移越高,查询变得越慢

所以

SELECT * FROM large ORDER BY `id`  LIMIT 0, 30 

远远少于

SELECT * FROM large ORDER BY `id` LIMIT 10000, 30 

这只能订购30条记录并且相同。所以这不是ORDER BY的开销 现在,当获取最新的30行时,大约需要180秒。如何优化该简单查询?

6 个答案:

答案 0 :(得分:179)

我自己也有同样的问题。鉴于您想要收集大量此类数据而不是特定的30集,您可能正在运行循环并将偏移量增加30。

所以你可以做的是:

  1. 保留一组数据的最后一个ID(30)(例如lastId = 530)
  2. 添加条件WHERE id > lastId limit 0,30
  3. 所以你总是可以有一个ZERO偏移量。你会对性能提升感到惊讶。

答案 1 :(得分:165)

通常情况下,较高的偏移会降低查询速度,因为查询需要对第一个OFFSET + LIMIT记录进行计数(并且只记录LIMIT个记录)。该值越高,查询运行的时间越长。

查询无法直接进入OFFSET,因为首先,记录的长度可能不同,其次,删除的记录可能存在间隙。它需要检查并统计每条记录。

假设idPRIMARY KEY表的MyISAM,您可以使用此技巧加快速度:

SELECT  t.*
FROM    (
        SELECT  id
        FROM    mytable
        ORDER BY
                id
        LIMIT 10000, 30
        ) q
JOIN    mytable t
ON      t.id = q.id

参见这篇文章:

答案 2 :(得分:16)

MySQL无法直接转到第10000条记录(或第80000字节作为建议),因为它不能假设它是打包/排序的(或者它具有1到10000的连续值)。虽然实际上可能是这种方式,但MySQL不能假设没有漏洞/间隙/删除的ID。

因此,正如鲍勃指出的那样,MySQL必须获取10000行(或遍历id上索引的第10000个条目),然后才能找到返回的30。

编辑:说明我的观点

请注意,虽然

SELECT * FROM large ORDER BY id LIMIT 10000, 30 

慢(呃)

SELECT * FROM large WHERE id >  10000 ORDER BY id LIMIT 30 

fast(呃),并且如果没有丢失id s(即间隙),则会返回相同的结果。

答案 3 :(得分:5)

两个查询的耗时部分是从表中检索行。从逻辑上讲,在LIMIT 0, 30版本中,只需要检索30行。在LIMIT 10000, 30版本中,将评估10000行并返回30行。可以在数据读取过程中进行一些优化,但请考虑以下内容:

如果查询中有WHERE子句,该怎么办?引擎必须返回所有符合条件的行,然后对数据进行排序,最后得到30行。

还要考虑ORDER BY序列中未处理行的情况。必须对所有符合条件的行进行排序,以确定要返回的行。

答案 4 :(得分:4)

我找到了一个有趣的例子来优化SELECT查询ORDER BY id LIMIT X,Y。 我有3500万行,所以花了2分钟才能找到一系列行。

这是诀窍:

select id, name, address, phone
FROM customers
WHERE id > 990
ORDER BY id LIMIT 1000;

只需将WHERE与您获得的最后一个ID相比,性能提高了很多。对我来说,这是从2分钟到1秒:)

其他有趣的技巧:http://www.iheavy.com/2013/06/19/3-ways-to-optimize-for-paging-in-mysql/

它也适用于字符串

答案 5 :(得分:2)

对于那些对比较和数字感兴趣的人:)

实验1:数据集包含约1亿行。每行包含几个BIGINT,TINYINT,以及两个TEXT字段(故意),这些字段包含大约1k个字符。

  • 蓝色:= SELECT * FROM post ORDER BY id LIMIT {offset}, 5
  • 橙色:= @Quassnoi的方法。 SELECT t.* FROM (SELECT id FROM post ORDER BY id LIMIT {offset}, 5) AS q JOIN post t ON t.id = q.id
  • 当然,第三个方法... WHERE id>xxx LIMIT 0,5在这里没有出现,因为它应该是恒定的时间。

实验2:相似,只是一行只有3个BIGINT。

  • 绿色:=前面的蓝色
  • 红色:=之前的橙色

enter image description here