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次
注意2:customers
是视图,不是基础table
情况 1:优化器可以在 ORDER BY
上使用索引。 LIMIT 10
将比 LIMIT 10000,10
更快,因为它可以更快地停止读取行。
情况 2:优化器不能(或选择不)为 ORDER BY
使用索引。在这种情况下,将收集整组行(在 WHERE
之后),对该组进行排序,然后才应用 OFFSET
和 LIMIT
。在这种情况下,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。总之,它不如使用可以同时处理WHERE
和ORDER BY
的复合索引。
如果您能设法完成案例 1,我建议您也 "remember where you left off" 使分页更加高效。参见 my Pagination blog。
我一直在为一个带有分页的系统搞乱查询性能,以尽可能快地选择数据,但我遇到了一些我不太明白的事情。据我所知,当使用带偏移量的限制时,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次
注意2:customers
是视图,不是基础table
情况 1:优化器可以在 ORDER BY
上使用索引。 LIMIT 10
将比 LIMIT 10000,10
更快,因为它可以更快地停止读取行。
情况 2:优化器不能(或选择不)为 ORDER BY
使用索引。在这种情况下,将收集整组行(在 WHERE
之后),对该组进行排序,然后才应用 OFFSET
和 LIMIT
。在这种情况下,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。总之,它不如使用可以同时处理WHERE
和ORDER BY
的复合索引。
如果您能设法完成案例 1,我建议您也 "remember where you left off" 使分页更加高效。参见 my Pagination blog。