避免在分页嵌套集上进行全表扫描

时间:2015-01-19 14:29:12

标签: mysql sql nested-sets

我有nested set设置,如下所示:

Node (Id, ParentId, LeftBounds, RightBounds, Level, Name)

LeftBounds有一个索引。

但是当我尝试选择分页结果时,

SELECT * FROM Node ORDER BY LeftBounds ASC LIMIT 500000, 1000

Sql进行全表扫描。我还应该注意一些其他方法来避免全表扫描吗?

这通常不是一个大问题,但是如果有几百万行的表,最后一页加载需要3-5秒。

1 个答案:

答案 0 :(得分:1)

您的LIMIT 5000000, 1000子句要求MySQL在您的结果集中排序结果,跳过50万个结果,然后显示1000. MySQL似乎已经决定最好用表扫描。这并不奇怪。

您可以尝试延迟加入操作。这样做的目的是减少需要订购的结果集的大小。它的工作原理如下。

SELECT Node.*
  FROM Node
  JOIN (
         SELECT id
           FROM Node
          ORDER BY LeftBounds ASC
          LIMIT 500000, 1000
       ) Subset ON Node.id = Subset.id
  ORDER BY Node.LeftBounds ASC

正如您所看到的,这会将您需要的大结果集限制为更少的列,特别是idLeftBounds。然后,它使用它找到的1000个不同id值的集合来检索完整记录。

如果你在(LeftBounds, id)上自己编写一个复合索引,你很可能会加快这个查询的速度。但它仍然必须跳过50万行,因此您的EXPLAIN可能会说您正在进行完整的索引扫描。

您可以使用此查询进行下一步加快速度,即摆脱SELECT *,而不是命名您需要的列。为什么这有帮助?因为它提供了一个覆盖索引的复合提示,可能有助于完全满足查询。您已经提到LeftBounds是唯一的,因此是JOIN条件的候选者。所以,让我们通过一个例子探讨这个问题。我们假设您希望在结果集中使用ParentId, LeftBounds, RightBounds, Level, Name。然后你可以使用这个查询:

SELECT Node.ParentId, Node.LeftBounds, 
       Node.RightBounds, Node.Level, Node.Name
  FROM Node
  JOIN (
         SELECT LeftBounds
           FROM Node
          ORDER BY LeftBounds ASC
          LIMIT 500000, 1000
       ) Subset ON Node.LeftBounds = Subset.LeftBounds
  ORDER BY Node.LeftBounds ASC

如果您有所需列的索引,MySQL可以满足索引中的查询。该索引应按此顺序包含这些列。

LeftBounds, ParentId, RightBounds, Level, Name

LeftBounds需要在索引中排在第一位,因为这是您用于随机访问索引的列。这里的要点是省略必须使用id列来访问表格。