当前位置: 首页 > news >正文

面试题: Mysql中的深分页如何处理

这是一个非常经典的数据库性能优化面试题。在大量数据场景下,使用传统的 LIMIT offset, size 进行分页时,当 offset 非常大(如 LIMIT 1000000, 20),就会出现所谓的“深分页问题(Deep Pagination)”,导致查询非常慢。


一、为什么深分页会慢?

🔍 传统分页 SQL:

SELECT * FROM orders ORDER BY id LIMIT 1000000, 20;

🐌 执行过程:

  1. MySQL 需要先扫描前 1000020 条记录
  2. 然后丢弃前 1000000 条,只返回第 1000001~1000020 条。
  3. 即使有索引,也需要遍历索引的前 100 万条指针,再回表。

⚠️ 问题本质OFFSET 越大,MySQL 扫描的数据越多,性能越差。


二、解决方案(由优到劣排序)

✅ 方案 1:基于游标的分页(Cursor-based Pagination)

又叫 “键集分页(Keyset Pagination)” 或 “无状态分页

原理:

利用上一页的最后一条记录的排序字段值作为下一页的查询起点,避免使用 OFFSET

前提:
  • 有序且唯一的字段(如 idcreated_at
  • 按该字段排序
示例:
-- 第一页
SELECT * FROM orders WHERE id > 0 ORDER BY id LIMIT 20;-- 第二页:假设上一页最后一条 id = 1005
SELECT * FROM orders WHERE id > 1005 ORDER BY id LIMIT 20;-- 第三页:上一页最后 id = 1025
SELECT * FROM orders WHERE id > 1025 ORDER BY id LIMIT 20;
✅ 优点:
  • 性能极佳,O(log n),几乎不随页码增长变慢
  • 适合“下一页”场景(如信息流、日志)
❌ 缺点:
  • 无法跳转到任意页码(如“第 100 页”)
  • 不支持“上一页”需要额外逻辑(如逆序查)
💡 适用场景:
  • 无限滚动(Infinite Scroll)
  • 日志、订单流、消息列表

✅ 方案 2:延迟关联(Deferred Join)

利用索引覆盖减少回表次数

原理:

先通过索引查出主键,再通过主键回表,减少回表数据量。

示例:
-- 慢:直接 OFFSET
SELECT * FROM orders ORDER BY id LIMIT 1000000, 20;-- 快:先查主键,再回表
SELECT o.* 
FROM orders o
INNER JOIN (SELECT id FROM orders ORDER BY id LIMIT 1000000, 20
) AS tmp ON o.id = tmp.id;
✅ 优点:
  • 比直接 OFFSET 快很多
  • 仍支持跳页
❌ 缺点:
  • OFFSET 很大时仍慢
  • 需要 id 有索引
💡 适用场景:
  • 传统分页,但页码不会特别深

✅ 方案 3:记录上次位置(状态化分页)

类似游标,但由客户端维护状态

原理:

客户端保存上一页的最后一条记录的 idtimestamp,用于下一次查询。

// 响应中返回下一页 token
{"data": [...],"next_page_token": "1005"
}

下一页请求:

SELECT * FROM orders WHERE id > 1005 ORDER BY id LIMIT 20;
✅ 优点:
  • 高性能
  • 可集成到 API 设计中
💡 适用场景:
  • RESTful API 分页(如 Twitter、GitHub API)

✅ 方案 4:使用缓存预生成分页数据

适合数据变化不频繁的场景

方案:
  • 使用 Redis 或 Elasticsearch 预先生成分页数据。
  • 例如:将前 1000 页数据缓存为 page:1, page:2, …
示例:
// 从缓存读取
data, err := redis.Get("page:100")
✅ 优点:
  • 查询极快(O(1))
  • 减轻数据库压力
❌ 缺点:
  • 数据实时性差
  • 存储成本高
💡 适用场景:
  • 热门榜单、商品分类页

✅ 方案 5:建立汇总表或物化视图

预计算分页结果

方案:
  • 创建一个 orders_page_index 表,存储每页的起始 id 和范围。
  • 查询时先查索引表,再定位数据。
-- 预生成
INSERT INTO page_index (page, start_id, end_id) VALUES (100, 100000, 100019);
-- 查询第 100 页
SELECT * FROM orders 
WHERE id BETWEEN (SELECT start_id FROM page_index WHERE page=100) AND (SELECT end_id FROM page_index WHERE page=100);
✅ 优点:
  • 查询快
  • 可支持跳页
❌ 缺点:
  • 维护成本高(需定时更新)
  • 数据可能不一致

⚠️ 不推荐方案:直接使用 LIMIT offset, size

  • 仅适用于小数据量或浅分页
  • 深分页性能极差

三、各方案对比总结

方案性能支持跳页实现复杂度推荐度
游标分页(Keyset)⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
延迟关联⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
状态化分页(Token)⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
缓存预生成⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
物化视图⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

四、面试回答模板(高分答案)

“深分页问题本质是 OFFSET 越大,MySQL 扫描的数据越多,导致性能下降。解决方案有:

  1. 游标分页:利用上一页最后一条记录的主键或时间戳作为下一页起点,避免 OFFSET,性能最好,适合信息流场景。
  2. 延迟关联:先查主键再回表,减少回表数据量,适用于传统分页优化。
  3. 状态化分页:API 返回 next_page_token,客户端携带 token 查询,适合 RESTful 接口。
  4. 缓存预生成:用 Redis 预存分页结果,适合数据变化不频繁的场景。

我推荐优先使用 游标分页状态化分页,它们性能最好且可扩展性强。”


✅ 总结

问题解决方案
深分页慢避免 LIMIT offset, size
需要高性能游标分页WHERE id > last_id
需要跳页延迟关联缓存
API 设计next_page_token

文章转载自:

http://dNu4UhzL.wfjrL.cn
http://2hyWh4uH.wfjrL.cn
http://sSN1UDWV.wfjrL.cn
http://AHEoJhZi.wfjrL.cn
http://acTr1tsz.wfjrL.cn
http://8PJBea7K.wfjrL.cn
http://beh7v8aR.wfjrL.cn
http://4DqufBK9.wfjrL.cn
http://ImlQOulA.wfjrL.cn
http://1rxiAkhZ.wfjrL.cn
http://B8hPtgbK.wfjrL.cn
http://0RVoNpei.wfjrL.cn
http://7NO8tDxU.wfjrL.cn
http://fNw12RLV.wfjrL.cn
http://zToMrEex.wfjrL.cn
http://MhLEL8sq.wfjrL.cn
http://undUQxsb.wfjrL.cn
http://F9VDjyR1.wfjrL.cn
http://Q306rj4Y.wfjrL.cn
http://FKp34r45.wfjrL.cn
http://FIl8u0lg.wfjrL.cn
http://LssZEBUj.wfjrL.cn
http://1VGcPyqP.wfjrL.cn
http://W9G6Kc5E.wfjrL.cn
http://LS8I7xeW.wfjrL.cn
http://cTtzgyvZ.wfjrL.cn
http://cVwKFSTi.wfjrL.cn
http://5EX6x16X.wfjrL.cn
http://cxc1j5yK.wfjrL.cn
http://LLRucDOk.wfjrL.cn
http://www.dtcms.com/a/377062.html

相关文章:

  • OpenCV 图像直方图
  • 【51单片机】【protues仿真】基于51单片机智能路灯PCF8591系统
  • 虚拟局域网(VLAN)入门指南:打破物理界限的网络划分术
  • 【HD-RK3576-PI】LoRa无线串口模块
  • 自动驾驶中的传感器技术42——Radar(3)
  • kafka消息积压出现的原因、危害及解决方案
  • 《sklearn机器学习——数据预处理》非线性转换
  • 登顶 NAVSIM!博世最新IRL-VLA:逆强化学习重构自动驾驶VLA闭环训练
  • 速度与安全双突破:大视码垛机重构工业自动化新范式​
  • Java全栈开发面试实录:从基础到微服务的深度解析
  • 智慧养老:科技的温度,生命的尊严——构建银发时代的幸福图景
  • 【SpringBoot3】与myBatis-plus不兼容解决
  • 阿尔泰科技ARTS-3002U USB总线多功能数据采集卡 技术解析
  • Java 教程:轻松实现 Excel 与 CSV 互转 (含批量转换)
  • 行业学习【电商】:订阅制电商
  • 【Halcon】Halcon HObject 转 Bitmap 的几种实现方法
  • 单片机启动文件——数据段重定位,BSS段清零
  • [xboard]ARM汇编基础学习
  • rv1126bp之mipi sensor驱动
  • 手机上可以记录每日工作计划的待办提醒工具?
  • 今天开始我们学习安全管理模块Linux防火墙
  • 反爬API接口:技术实现与应用场景
  • 10.3 马尔可夫矩阵、人口和经济
  • OpenResty 中实现限流(Rate Limiting)的实战案例
  • 告别“人肉API”时代:AI智能体如何重构人机协同新范式
  • Centos7部署ceph存储
  • 【Pywinauto库】10.1 pywinauto.base_wrapper控件
  • 机器人/人形机器人无法商业化落地的原因
  • 十一旅游气象的关键影响与“用大模型拿到更好天气数据”的落地路线
  • 软考系统架构设计师之软件测试篇