MySQL Select 有偏移比没有偏移快

MySQL Select with Offset is Faster Than no Offset

我一直在为一个带有分页的系统搞乱查询性能,以尽可能快地选择数据,但我遇到了一些我不太明白的事情。据我所知,当使用带偏移量的限制时,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 */

但是,如果我使用内部连接将 table 连接到自身,仅使用 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是视图,不是基础table

情况 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)。或者,优化器选择使用其他索引,那么它是一个慢的Case 2。总之,它不如使用可以同时处理WHEREORDER BY的复合索引。

如果您能设法完成案例 1,我建议您也 "remember where you left off" 使分页更加高效。参见 my Pagination blog

More on creating INDEXes.