2

我有一个包含大约 300 万条记录的简单表。我做了必要的索引,我也强制索引 PRIMARY 但仍然不起作用。它搜索几乎所有 300 万行,而不是使用索引来执行这一行(record_id 是 INT 自动增量):

EXPLAIN SELECT record_id
FROM myrecords
FORCE INDEX (
PRIMARY )
ORDER BY record_id ASC
LIMIT 2955900 , 300

id  select_type     table     type  possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE          myrecords index NULL            PRIMARY 4           NULL    2956200 Using index

指数是

Keyname Type    Unique  Packed  Column      Cardinality Collation   Null
PRIMARY BTREE   Yes     No      record_id   2956742     A           No  

我想知道为什么这个 FORCED 索引没有被正确使用。

如果 ASC 和 DESC 都尝试不强制索引“主要”,结果是相同的。表已修复-优化-分析。没运气。

查询需要超过一分钟才能执行!

我所期望的:查询应该只处理 300 行,因为该列已被索引。正如您在第一个代码格式化块中看到的那样,几乎不是全部 300 万个(向右滚动一点)

4

1 回答 1

7

索引查找是按,而不是按位置。索引可以搜索值 2955900,但您并不要求这样做。您要求查询从表中第 2955900 行的偏移量开始。

优化器不能假设所有主键值都是连续的。因此,第 2955900 行的值很可能远高于此值。

即使主键值是连续的,您也可能有一个仅匹配 45% 行的 WHERE 条件。在这种情况下,第 2955900 行的 id 值将远远超过 id 值 2955900。

换句话说,对 id 值 2955900 的索引查找将不会传递第 2955900 行。

所以 MySQL 不能使用索引作为限制的偏移量。它必须扫描行以计算它们,直到达到偏移+限制行。

MySQL 确实有与 LIMIT 相关的优化,但它更多的是在达到要返回的行数时停止表扫描。优化器可能仍会在 EXPLAIN 计划中报告它可能需要扫描整个表。

关于FORCE INDEX的一个常见误解是它强制使用索引。:-) 事实上,如果查询不能使用索引(或者如果可用索引对此查询没有任何好处),FORCE INDEX 没有任何作用。


回复您的评论:

分页是数据驱动的 Web 应用程序的常见祸根。尽管此功能很常见,但优化并不容易。这里有一些提示:

  • 为什么要使用偏移量 2955900 进行查询?您真的希望用户筛选那么多页面吗?大多数用户在几页后放弃(具体多少取决于应用程序的类型和数据)。

  • 减少查询次数。您的分页功能可以获取前 5-10 页,即使它只向用户显示第一页。缓存其他页面,假设用户将前进几页。只有当它们通过缓存的页面集时,您的应用程序才必须执行另一个查询。您甚至可以在客户端浏览器上以 Javascript 缓存所有 10 个页面,因此单击“下一步”对他们来说是即时的(至少对于前几页而言)。

  • 不要在任何用户界面上放置“最后一个”按钮,因为人们会出于好奇而点击它。请注意,Google 有一个“下一步”按钮,但没有“最后一个”按钮。所以 UI 本身不鼓励人们运行高偏移量的低效查询。

  • 如果用户一次前进一页,则在下一页查询的 WHERE 子句中使用上一页返回的最高 id 值。即以下确实使用索引,即使没有 FORCE INDEX 提示:

    SELECT * FROM thistable WHERE id > 544 LIMIT 20
    
于 2013-02-28T20:23:27.270 回答