我怎样才能加快在LIMIT子句中有大偏移量的MySQL查询?

LIMIT一个大的偏移量的MySQL SELECT时,我遇到性能问题:

 SELECT * FROM table LIMIT m, n; 

如果偏移量m大于100万,则操作非常缓慢。

我不得不使用limit m, n ; 我不能使用id > 1,000,000 limit n

我如何优化这个声明以获得更好的性能?

也许你可以创build一个索引表,它提供了与目标表中的键相关的顺序键。 然后,您可以将此索引表连接到目标表,并使用where子句更高效地获取所需的行。

 #create table to store sequences CREATE TABLE seq ( seq_no int not null auto_increment, id int not null, primary key(seq_no), unique(id) ); #create the sequence TRUNCATE seq; INSERT INTO seq (id) SELECT id FROM mytable ORDER BY id; #now get 1000 rows from offset 1000000 SELECT mytable.* FROM mytable INNER JOIN seq USING(id) WHERE seq.seq_no BETWEEN 1000000 AND 1000999; 

在互联网上的某个地方有一个博客post,你应该如何让select的行尽可能的紧凑,这就是:ids; 并产生完整的结果应该反过来提取所有你想要的数据只有你select的行

因此,SQL可能是类似的(未经testing,我不确定它实际上会有什么好处):

 select A.* from table A inner join (select id from table order by whatever limit m, n) B on A.id = B.id order by A.whatever 

如果你的SQL引擎太原始了,不允许这种types的SQL语句,或者它不能改善任何事情,那么可能是值得的,把这个单一语句分解成多个语句,并将这个ID捕获到一个数据结构中。

更新 :我发现我正在谈论的博客文章:这是杰夫·阿特伍德的“所有抽象是失败的抽象”编码恐怖。

如果logging很大,则缓慢可能来自加载数据。 如果id列被索引,那么select它会快得多。 然后,您可以使用IN子句为适当的ID执行第二个查询(或者可以使用来自第一个查询的最小和最大ID来制定WHERE子句)。

慢:

 SELECT * FROM table ORDER BY id DESC LIMIT 10 OFFSET 50000 

快速:

 SELECT id FROM table ORDER BY id DESC LIMIT 10 OFFSET 50000 SELECT * FROM table WHERE id IN (1,2,3...10) 

Paul Dixon的回答确实是解决问题的方法,但是您必须维护序列表并确保没有行间隙。

如果这是可行的,更好的解决scheme是简单地确保原始表没有行间隙,并从id 1开始。然后使用id分页来获取行。

SELECT * FROM table A WHERE id> = 1 AND id <= 1000;
SELECT * FROM table A WHERE id> = 1001 AND id <= 2000;

等等…

我不认为有任何需要创build一个单独的索引,如果你的表已经有一个。 如果是这样的话,那么你可以通过这个主键来定购,然后使用这个键的值来遍历:

 SELECT * FROM myBigTable WHERE id > :OFFSET ORDER BY id ASC; 

另一个优化不是使用SELECT *,而是使用ID,这样它就可以简单地读取索引,而不必再find所有数据(减lessIO开销)。 如果你需要一些其他的列,那么也许你可以将这些添加到索引,使他们与主键读取(这将很有可能被保存在内存中,因此不需要光盘查找) – 虽然这不会是适当的对于所有的情况,所以你将不得不有一个发挥。

我写了一篇更详细的文章:

http://www.4pmp.com/2010/02/scalable-mysql-avoid-offset-for-large-tables/

最近我遇到了这个问题。 问题是两个部分来解决。 首先,我不得不在我的FROM子句中使用一个内部select,这个select对我来说只是在主键上进行限制和抵消:

 $subQuery = DB::raw("( SELECT id FROM titles WHERE id BETWEEN {$startId} AND {$endId} ORDER BY title ) as t"); 

然后我可以使用它作为我查询的一部分:

 'titles.id', 'title_eisbns_concat.eisbns_concat', 'titles.pub_symbol', 'titles.title', 'titles.subtitle', 'titles.contributor1', 'titles.publisher', 'titles.epub_date', 'titles.ebook_price', 'publisher_licenses.id as pub_license_id', 'license_types.shortname', $coversQuery ) ->from($subQuery) ->leftJoin('titles', 't.id', '=', 'titles.id') ->leftJoin('organizations', 'organizations.symbol', '=', 'titles.pub_symbol') ->leftJoin('title_eisbns_concat', 'titles.id', '=', 'title_eisbns_concat.title_id') ->leftJoin('publisher_licenses', 'publisher_licenses.org_id', '=', 'organizations.id') ->leftJoin('license_types', 'license_types.id', '=', 'publisher_licenses.license_type_id') 

我第一次创build这个查询时,我在MySql中使用了OFFSET和LIMIT。 这工作得很好,直到我超过第100页,然后偏移开始变得难以忍受的缓慢。 在我的内部查询中将其更改为BETWEEN可加快任何页面的速度。 我不确定为什么MySql没有加快OFFSET,但似乎在callback。