简短情景:一张记录超过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秒。如何优化该简单查询?
答案 0 :(得分:179)
我自己也有同样的问题。鉴于您想要收集大量此类数据而不是特定的30集,您可能正在运行循环并将偏移量增加30。
所以你可以做的是:
WHERE id > lastId limit 0,30
所以你总是可以有一个ZERO偏移量。你会对性能提升感到惊讶。
答案 1 :(得分:165)
通常情况下,较高的偏移会降低查询速度,因为查询需要对第一个OFFSET + LIMIT
记录进行计数(并且只记录LIMIT
个记录)。该值越高,查询运行的时间越长。
查询无法直接进入OFFSET
,因为首先,记录的长度可能不同,其次,删除的记录可能存在间隙。它需要检查并统计每条记录。
假设id
是PRIMARY 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
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。