【问题标题】:MySQL Select with Offset is Faster Than no OffsetMySQL Select with Offset 比没有 Offset 快
【发布时间】:2015-08-19 05:02:33
【问题描述】:

我一直在搞乱分页系统的查询性能,以使数据选择尽可能快,但我遇到了一些我不太明白的事情。据我所知,当使用带有偏移量的限制时,MySQL 必须遍历偏移量之前的每一行,然后丢弃它们,因此理论上,偏移量为 10,000 的查询会比没有偏移量的查询慢得多,这通常是正确的像这种情况

select SQL_NO_CACHE * from `customers` where `NetworkID`='\func uuid()' 
    order by `DateTimeAdded` desc limit 0, 100;
/* finishes in 2.497 seconds */

 select SQL_NO_CACHE * from `customers` where `NetworkID`='\func uuid()' 
   order by `DateTimeAdded` desc limit 10000, 100;
 /* finishes in 2.702 seconds */

但是,如果我使用内连接将表连接到自身,并且仅使用 UserID 列进行排序和限制,则偏移量为 10,000 的速度始终,而不是没有偏移量,这完全难倒我。这里的例子是

select SQL_NO_CACHE * from `customers` 
    inner join (select `UserID` from `customers` where `NetworkID`='\func uuid()' 
        order by `DateTimeAdded` desc limit 100) 
    as `Results` using(`UserID`)
/* finishes in 1.133 seconds */

select SQL_NO_CACHE * from `customers` 
    inner join (select `UserID` from `customers` where `NetworkID`='\func uuid()' 
        order by `DateTimeAdded` desc limit 10000, 100) 
    as `Results` using(`UserID`)
/* finishes in 1.120 seconds */

为什么使用偏移量的查询总是比不使用偏移量的查询快?


解释:

我在此处发布了一个 Google 文档电子表格,其中包含 explains 内容 here

注意:以上测试是在 PHP 循环中完成的,每次循环 20 次

注意2customers 是视图,而不是基表

【问题讨论】:

  • 尝试不同的偏移量,看看你是否得到相同的趋势。可能是这个特定的偏移量有一个非常简单的连接
  • 我有,如果我用 30,000 甚至 30,000 执行此操作,它仍然始终比没有偏移量的查询快
  • optimize这个表,看看是不是一样(先中和所有未知因素)
  • @Brian Leishman:我想知道您是否总是以相同的顺序运行这两个查询以进行测试。
  • 也想到了@a1ex07,如果我切换查询的顺序,有偏移的还是赢

标签: mysql performance inner-join limit offset


【解决方案1】:

案例 1:优化器可以使用ORDER BY 上的索引。 LIMIT 10 会比 LIMIT 10000,10 更快,因为它可以更快地停止读取行。

案例 2:优化器不能(或选择不)为ORDER BY 使用索引。在这种情况下,将收集整组行(在WHERE 之后),对该组进行排序,然后才应用OFFSETLIMIT。在这种情况下,OFFSET 的值几乎没有区别;大部分时间都花在了获取行、过滤和排序上。

INDEX(x,y)
SELECT ... WHERE x=2               ORDER BY y LIMIT ... -- case 1
SELECT ... WHERE x=2 AND deleted=0 ORDER BY y LIMIT ... -- case 2

INDEX(NetworkID, DateTimeAdded)         -- composite
SELECT ... WHERE NetworkID='...' ORDER BY DateTimeAdded DESC ... -- Case 1

INDEX(NetworkID), INDEX(DateTimeAdded)  -- separate
SELECT ... WHERE NetworkID='...' ORDER BY DateTimeAdded DESC ... -- Case 3

案例 3 可能类似于案例 1,因为它可能使用INDEX(DateTimeAdded)。或者,优化器选择使用其他索引,那么它是一个慢的情况2。无论如何,它不如使用可以处理WHEREORDER BY的复合索引。

如果你能设法进入案例 1,我建议你也“记住你离开的地方”,以使分页更加高效。见my Pagination blog

More on creating INDEXes.

【讨论】:

  • 是的,我读过很多关于分页的文章,并且遇到了“记住我离开的地方”的策略,但在我的情况下,这些策略都不会真正帮助我。原因 1 是这总是要求我有我根本无法使用的数字数据,因为我有多个合并的服务器。原因 2,是我显示了数据的过滤子集,用户可以以任何方式对其进行排序,这意味着数据从不按数字顺序。
  • 我是否至少解释了为什么OFFSET 不会影响速度? (也就是说,大部分时间发生在应用OFFSET之前。)
  • 是的,我相信你已经解释了为什么我的查询看起来很奇怪,谢谢你!
猜你喜欢
  • 2021-10-01
  • 2016-07-11
  • 1970-01-01
  • 2022-12-27
  • 2011-11-12
  • 2021-03-23
  • 1970-01-01
  • 2018-02-27
  • 2020-08-19
相关资源
最近更新 更多