我们知道了延迟关联是深分页优化的利器——但它并不是万能的!在以下场景中,延迟关联也会“束手无策”:
1、排序字段无索引:子查询触发文件排序(Filesort),性能雪崩;
2、超大数据量:即使有索引,Offset达到百万级时,索引扫描仍可能超时;
3、强制跳页需求:用户要求直接跳转到第N页(N极大)。
怎么办? 今天揭秘三大终极方案,专治延迟关联搞不定的“深分页绝症”!
一、杀手锏1:游标分页(Cursor-based Pagination)—— 抛弃OFFSET,用时间换空间
1.1 核心原理
- 不用OFFSET,而是基于有序且唯一的游标字段(如时间戳+ID)实现分页;
- 每次查询只取上一页最后一条记录的游标值,作为下一页的起点。
1.2 示例场景
用户需要分页查看订单列表(按创建时间倒序):
-- 第一页(最新10条)
SELECT * FROM orders
ORDER BY create_time DESC, id DESC
LIMIT 10;
-- 第二页:用第一页最后一条的create_time和id作为游标
SELECT * FROM orders
WHERE create_time <= '2023-10-01 12:00:00' AND id < 100
ORDER BY create_time DESC, id DESC
LIMIT 10;
1.3 性能优势
- 时间复杂度O(1):直接通过游标定位数据,无需扫描前N行;
- 无OFFSET爆炸问题:翻页到第100万页,速度仍和第一页一致。
1.4 适用场景
- 支持“上一页/下一页”,但不支持随机跳页;
- 排序字段必须唯一且有序(如时间戳+自增ID)。
1.5 缺点
- 业务需改造:前端需保存游标值;
- 无法直接跳转到指定页码。
二、杀手锏2:预计算分页(Pre-computed Pagination)—— 空间换时间,提前“算好答案”
2.1 核心原理
- 提前计算分页结果并存储(如物化视图、临时表、Redis缓存);
- 查询时直接读取预存结果,绕过实时计算。
2.2 实现方案
方案1:物化视图(Materialized View)
-- 创建物化视图(需借助工具或定时任务)
CREATE TABLE orders_page_cache AS
SELECT id, ROW_NUMBER() OVER (ORDER BY create_time DESC) AS page_num
FROM orders;
-- 查询第100页(每页10条)
SELECT * FROM orders
WHERE id IN (
SELECT id FROM orders_page_cache
WHERE page_num BETWEEN 1000 AND 1010
);
方案2:Redis缓存分页键
# 预生成分页Key(如每页存储前10个ID)
for page in range(1, total_pages):
ids = db.query("SELECT id FROM orders ORDER BY create_time DESC LIMIT 10 OFFSET {page}")
redis.set(f"orders:page:{page}", ids)
# 查询时直接读缓存
ids = redis.get(f"orders:page:{100}")
result = db.query("SELECT * FROM orders WHERE id IN (ids)")
2.3 性能优势
- 查询耗时接近0:直接读取预存结果;
- 适合数据变化频率低的场景(如历史订单)。
2.4 缺点
- 存储成本高:需额外存储分页数据;
- 数据更新时需同步维护预计算结果。
三、杀手锏3:业务妥协(Business Compromise)—— 放弃完美,换取生存
3.1 核心思想
技术解决不了的问题,用业务设计化解! 通过交互或规则限制,绕过深分页需求。
3.2 实战方案
方案1:禁止跳页,仅允许“上一页/下一页”
- 交互设计:隐藏页码输入框,只保留“加载更多”按钮;
- 技术实现:强制使用游标分页。
方案2:分页阈值限制
- 规则:最多允许查看前1000页(每页10条,即1万条);
- 提示语:“仅展示最新1万条数据,请使用搜索筛选”。
方案3:化整为零,分而治之
- 按时间范围拆分:先让用户选择“2023年10月”,再分页查询该月数据;
- 按分类筛选:先选择商品类目,再分页查询该类目下的数据。
3.3 适用场景
- 对用户体验影响可控的场景;
- 数据量极大且无法通过技术优化的“死局”。
四、终极对决:三大方案如何选?
方案 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
方案 | 适用场景 | 优点 | 缺点 |
游标分页 | 需连续翻页(如社交动态流) | 性能极致,无OFFSET问题 | 不支持跳页 |
预计算分页 | 数据变化少,需高频访问(如报表) | 查询速度极快 | 存储成本高,维护复杂 |
业务妥协 | 数据量极大,技术优化成本高 | 简单粗暴,快速落地 | 用户体验降级 |
决策建议:
1、 1. 优先尝试游标分页(性能与体验平衡);
2、 2. 静态数据用预计算;
3、 3. 实在不行再业务妥协。
五、总结:没有银弹,只有权衡
深分页的本质:在数据量、性能、用户体验之间寻找平衡;
优化哲学:
能加索引的不要硬扛;
能改交互的不要死磕技术;
能预计算的不要实时计算。