今天聊一个分库分表后的经典难题:分页查询 。
很多伙伴在面试或者工作里都被问过这个问题,如果没理清楚,容易一头雾水。那我们就用最简单的大白话,把它捋清楚说明白。
1. 不是所有场景都需要分页方案
分页问题只有在 跨库或跨表查询 时才会变复杂。
举个例子:
- 如果查询条件里总是带 分片键(比如查某个用户ID的数据,或者查某个时间片的数据),那它就是查某一个分片的数据。
- 这种情况和查单表一模一样,不存在什么分页难题。
👉 所以第一步一定要问:这次查询到底是不是跨分片的?
2. 三种主流解决方案
当我们真的遇到跨分片分页时,常见的有三种办法:
方案 | 核心思想 | 优点 | 缺点 |
---|---|---|---|
方案1:专用中间件/数据库 (如 Elasticsearch、TiDB) | 把分库分表的数据同步到更适合做复杂查询的系统里,由它来处理分页。 | 性能高,工业界常用方案。 | 要保证数据同步一致性(MySQL → ES)。 |
方案2:开源框架 (如 ShardingSphere) | 应用还是写"单表SQL",框架在背后把SQL拆开,去各个分片拉数据,再在内存里排序、分页。 | 对应用几乎零改动,开发体验好。 | 深度翻页性能差。 |
方案3:业务妥协 | 跟产品经理沟通,限制查询范围,不搞跨分片分页。例如只查当前分片,或者提示用户"仅显示部分结果"。 | 最简单,直接回避问题。 | 对业务体验可能有影响。 |
3. 全局查找法的真相
面试中最喜欢问的就是方案2里提到的 全局查找法。
⚙️ 原理图
flowchart TD
A[客户端 SQL LIMIT offset,size] --> B[各分片执行 LIMIT 0, offset+size]
B --> C[汇总数据]
C --> D[内存归并排序]
D --> E[取 offset 之后的 size 条数据]
⚠️ 问题
-
正确性没问题,但如果查询第 100,000 页:
- 每个分片要查几十万甚至上百万条数据。
- 内存里还要排序和裁切。
- 极容易 OOM(内存溢出),速度慢到怀疑人生。
所以: 👉 全局查找法能用,但翻页浅一点还行,深度翻页会死。
4. 优化:禁止跳页,顺序翻页更香
解决深度翻页问题的最佳实践是:基于上一页最大 ID 翻页。
✅ 做法
-
第 1 页:
sqlSELECT * FROM table ORDER BY id LIMIT size;
-
第 2 页:
sqlSELECT * FROM table WHERE id > {上一页最大id} ORDER BY id LIMIT size;
👍 优点
- 每次只查一页,性能极好。
- 和 offset 大小无关,不怕深度翻页。
👎 缺点
- 不能直接跳到第 100 页。
- 只能一页一页往下翻,像刷微博/抖音一样。
👉 但对大多数场景(信息流、消息列表),完全够用。
5. 不推荐的方案:二次查询法
有些文章会提到"二次查询法"。 它实现复杂,还可能遇到数据分布不均的问题,结果不可靠。
一句话:不推荐使用。
6. 面试怎么答(STAR 法则)
如果在面试被问到,可以按 STAR 模型来回答:
-
S(情境): 系统做了分库分表后,跨分片分页成了难题。
-
T(任务): 需要一个能保证全局分页正确性的方案。
-
A(行动):
- 首先评估业务,能不能限制查询范围,直接规避问题。
- 如果必须实现,首推用 Elasticsearch/TiDB 这类系统。
- 如果必须在数据库层面搞,就用 ShardingSphere 的全局查找法,但要说明深度翻页问题。
- 针对深度翻页,再提出"基于上一页最大ID"的优化。
-
R(结果): 既能满足业务需求,也能保证性能稳定性。
总结
-
不是所有分库分表都有分页问题,只有跨分片才需要考虑。
-
常见三类方案:
- 中间件/数据库(推荐)
- 开源框架(方便但有性能坑)
- 业务妥协(直接回避)
-
全局查找法能用,但深度翻页会炸。
-
最佳实践:禁止跳页 + 顺序翻页。
一句话总结:
能规避就规避,能交给专用系统就交给专用系统,实在要做就用连续翻页。