当前位置: 首页> 财经> 访谈 > 北京病例最新消息今天_北京互联网公司招聘信息_千锋教育郑州校区_谷歌搜索引擎首页

北京病例最新消息今天_北京互联网公司招聘信息_千锋教育郑州校区_谷歌搜索引擎首页

时间:2025/7/14 2:59:06来源:https://blog.csdn.net/qq_55675216/article/details/145881236 浏览次数:0次
北京病例最新消息今天_北京互联网公司招聘信息_千锋教育郑州校区_谷歌搜索引擎首页

原始分页

SELECT * FROM tb_sku ORDER BY id LIMIT 9000000, 10;

问题分析:
需要先扫描前 9000000+10 条数据,然后丢弃前 9000000 条 (关键点在这里,需要优化)
即便 id 有索引,也需要回表 9000010 次(每次都要读取磁盘数据)
当 offset 极大时,性能急剧下降(本质是 O(n) 复杂度)

优化分页

SELECT t.* 
FROM tb_sku t, (SELECT id FROM tb_sku ORDER BY id LIMIT 9000000, 10) a 
WHERE t.id = a.id;

1、子查询优先走覆盖索引​
子查询 SELECT id FROM tb_sku 只查 id 列
若 id 是主键或唯一索引,直接从索引树获取数据​(覆盖索引,无需回表)
仅需扫描索引树的 9000010 个节点(比全表扫描快得多)

2、外层查询精准回表​
通过 WHERE t.id = a.id 的主键关联
只需回表 10 次(最终需要的 10 条数据)

在这里插入图片描述

问题
为什么传统方法需要 回表 9000010 次 。id索引不就是 聚集索引吗 ,那不就是覆盖索引吗

​正确认知:在InnoDB中,聚集索引的叶子节点直接存储完整数据行​(即数据页和索引页合一)。因此,当查询通过聚集索引定位到记录时,确实不需要回表。

​关键矛盾点:问题出在LIMIT offset, count的执行逻辑,而不是索引本身的结构。

SELECT * FROM tb_sku ORDER BY id LIMIT 9000000, 10;

1、索引扫描​
从聚集索引(B+树)的根节点开始,按ORDER BY id顺序遍历叶子节点链。
​需要扫描前 9000000+10 条记录的索引项​(即使不需要数据,也要走索引链路定位到第9000000条的位置)。

2、 ​数据页访问​
虽然聚集索引的叶子节点包含完整数据,但每次索引项扫描都需要访问对应的数据页​(物理磁盘上的页读取)。
即使只需要id字段,存储引擎也必须加载整个数据页到内存。

3、 ​资源消耗​
假设每条记录大小为1KB,数据页大小为16KB,则每页存储约16条记录。
扫描9000010条记录需要访问约 9000010 / 16 ≈ ​562,501 个数据页。
​每次页访问都是一次磁盘I/O​(即使数据在内存中,也需要遍历页链表)。

为什么说是“回表”?
​狭义回表:当使用非聚集索引(二级索引)时,需要根据索引中的主键值回到聚集索引查数据。

​广义回表(此处场景)​:
即使id是聚集索引,执行LIMIT offset, count时:
​需要遍历索引链路找到第 N 条的位置​(类似链表遍历)
​每次索引项访问都会触发数据页加载​(本质是顺序扫描数据页)
这本质上等效于通过索引路径强制触发全表扫描,因此可以看作一种特殊的“回表”逻辑。

再优化:游标分页

SELECT * FROM tb_sku 
WHERE id > {last_id}  -- 用上一页最后一条id作为游标
ORDER BY id 
LIMIT 10;

直接通过B+树的>条件定位到起始位置(时间复杂度O(log n))
无需扫描前面的900万条记录

关键字:北京病例最新消息今天_北京互联网公司招聘信息_千锋教育郑州校区_谷歌搜索引擎首页

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: