mysql:非常简单的 SELECT id ORDER BY LIMIT 不会按预期使用 INDEX (?!)

Sha*_*rky 1 mysql indexing sql-order-by

我有一个包含大约 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
Run Code Online (Sandbox Code Playgroud)

该指数是

Keyname Type    Unique  Packed  Column      Cardinality Collation   Null
PRIMARY BTREE   Yes     No      record_id   2956742     A           No  
Run Code Online (Sandbox Code Playgroud)

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

在不强制索引“主要”的情况下,ASC 和 DESC 都尝试过,结果是一样的。表已修复-优化-分析。没运气。

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

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

Bil*_*win 6

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

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

即使主键值是连续的,您也可能有一个 WHERE 条件仅匹配,例如,45% 的行。在这种情况下,第 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
    
    Run Code Online (Sandbox Code Playgroud)