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

Rah*_*man 158 mysql performance sql-order-by limit

简而言之:一个包含超过1600万条记录的表[大小为2GB].使用ORDER BY*primary_key时,使用SELECT的LIMIT偏移越高,查询变得越慢

所以

SELECT * FROM large ORDER BY `id`  LIMIT 0, 30 
Run Code Online (Sandbox Code Playgroud)

远远不及

SELECT * FROM large ORDER BY `id` LIMIT 10000, 30 
Run Code Online (Sandbox Code Playgroud)

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

小智 201

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

所以你可以做的是:

  1. 保留一组数据的最后一个id(30)(例如lastId = 530)
  2. 添加条件 WHERE id > lastId limit 0,30

所以你总是可以有一个ZERO偏移量.性能改进会令您惊讶.

  • 只是注意限制/偏移通常用于分页结果,并且持有lastId根本不可能,因为用户可以跳转到任何页面,而不是总是下一页.换句话说,偏移通常需要根据页面和限制动态计算,而不是遵循连续模式. (25认同)
  • +1,这个答案值得更多的信任 (11认同)
  • 对于所有人来说,这可能并不明显,只有当你的结果集按照那个键排序时才会有效(按降序排列相同的想法有效,但是更改> lastid到<lastid.)无论是否为主键或其他字段(或字段组). (8认同)
  • 男人。你是一个救星。当我尝试你的答案时,我有 500 万条数据,需要大约 90 分钟才能处理所有带有偏移和限制的数据。daamn 只需 9 分钟即可处理,谢谢你。谢谢你!! (5认同)
  • 我在http://mysql.rjweb.org/doc.php/pagination中更详细地谈论"记住你离开的地方" (3认同)
  • @Lanti让我们假设第563页从偏移量563 * 30 = 16890开始,因为在OP的示例中30是页面大小,并假设页面编号从0开始。进一步假设列“id”是唯一的并且已建立索引。然后执行 select id from large order by id limit 16889, 1 来读取 Page 562 最后一行的 id。这应该是相当有效的,因为只涉及索引。现在您有了“lastId”来继续选择下一页。 (2认同)

Qua*_*noi 182

通常,较高的偏移会减慢查询速度,因为查询需要计算第一个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
Run Code Online (Sandbox Code Playgroud)

看到这篇文章:

  • MySQL"早期行查找"行为就是为什么它说话这么久的答案.通过您提供的技巧,只有匹配的ID(由索引直接)绑定,从而节省了太多记录的不需要的行查找.这就是伎俩,万岁! (7认同)
  • @ f055:答案说"加速",而不是"瞬间".你读过答案的第一句话了吗? (6认同)
  • @harald:"你的工作"究竟是什么意思?这是一种纯粹的性能提升.如果"ORDER BY"没有可用的索引,或者索引覆盖了您需要的所有字段,则不需要此解决方法. (4认同)
  • 是否有可能为InnoDB运行这样的东西? (3认同)
  • @aurora - 一旦你必须在`id`上扫描这么多''技巧'变慢的索引,这个技巧就会慢下来.真正的"修复"是"记住你离开的地方". (2认同)
  • @Lanti:请将其作为一个单独的问题发布,不要忘记用`postgresql`标记它.这是MySQL特定的答案. (2认同)

Rie*_*sio 17

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

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

编辑:说明我的观点

请注意,虽然

SELECT * FROM large ORDER BY id LIMIT 10000, 30 
Run Code Online (Sandbox Code Playgroud)

会很慢(呃),

SELECT * FROM large WHERE id >  10000 ORDER BY id LIMIT 30 
Run Code Online (Sandbox Code Playgroud)

很快(呃),并且只要没有缺失ids(即间隙)就会返回相同的结果.

  • 这是对的.但由于它受"id"的限制,为什么id在索引(主键)中需要这么长时间?优化器应直接引用该索引,然后获取具有匹配ID的行(来自该索引) (2认同)

小智 7

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

这是窍门:

select id, name, address, phone
FROM customers
WHERE id > 990
ORDER BY id LIMIT 1000;
Run Code Online (Sandbox Code Playgroud)

只需将WHERE和最后一个ID放在一起即可提高性能。对我来说是2分钟到1秒:)

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

它也适用于字符串

  • 这仅适用于没有删除数据的表 (2认同)
  • @miro 只有当您假设您的查询可以在随机页面上进行查找时,这才是正确的,我不相信这张海报是这样假设的。虽然我不喜欢在大多数实际情况下使用这种方法,但只要您始终将其基于获得的最后一个 id,这将适用于间隙。 (2认同)

bob*_*obs 5

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

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

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