我在表中有29900000条记录,偏移消耗了太多的查询执行时间
SELECT * FROM table_records LIMIT 50 OFFSET 1999950
this query taking 33.087 sec
我已将偏移量更改为2000000
SELECT * FROM table_records LIMIT 50 OFFSET 2000000
this query taking 2.030 sec
的解释
EXPLAIN SELECT * FROM table_records LIMIT 50 OFFSET 29941250
id | select_type | table | type | possible_keys | key | key_len | ref |rows | Extra
1 | SIMPLE | table_records | index | (NULL) | PRIMARY | 4 | (NULL) |29900771 |
我已删除偏移设置为限制
SELECT * FROM table_records LIMIT 50
this query taking 0.002 sec
赞赏任何建议或想法。
答案 0 :(得分:2)
这都是关于缓存的。
简单地说,OFFSET
很糟糕。它必须读取并忽略所有“偏移”行,然后传递“限制”行。
当跳过行时,它必须获取行 - 如果它们在磁盘上,这需要时间;如果它们被缓存在RAM中,它会快得多。 (通常快10倍。)
在您的情况下发生了可能:第一个查询在RAM中找到了很少的行(如果有的话),因此它必须包含大部分或全部1999950行。
然后你的第二个查询快速扫描1999950行,然后从磁盘中取出最后的50行。 (或者可能最后50个已经出现,因为I / O的单位是记录的“块”。)
使用LIMIT
和/或OFFSET
,EXPLAIN
很少提供任何线索 - 它通常会提供表格中总行数的估算值
您的示例还有另一个问题......您没有ORDER BY
。因此,引擎可以随意提供它喜欢的任何行。通常它是可预测的,但有时你会得到惊喜。
但是,一旦添加ORDER BY
,可能需要成为临时表,并在获取第一条记录之前对进行排序!也就是说,SELECT ... ORDER BY .. LIMIT 50
可能与所有其他人一样慢 - 如果您按照索引等不方便的顺序进行排序。
见how OFFSET sucks when paginating web pages。这包括“记住你离开的地方”的解决方法。并且this显示了如何有效地获得接下来的1000行,即使在ID中存在间隙。
答案 1 :(得分:1)
有一个解决方案可以更快地完成它,但只有当你有一个主键int并且在这个表中没有间隙时它才会起作用。我的意思是你不允许删除。在这种情况下,您的查询将如下所示:
SELECT * FROM table_records where id >= 1999950 order by id LIMIT 50;