京东二面:分库分表后翻页100万条,怎么设计?答对直接拿下P7!

182 阅读5分钟

沉默是金,总会发光

大家好,我是沉默

你有没有被问过这个经典问题:

“如果表里数据量特别大,翻页翻到100万条,怎么优化分页查询?”

别以为这只是简单的 LIMIT + OFFSET,实际上这道题背后藏着对数据库原理理解、系统架构设计能力和实战经验的三重考验。

今天我们就从面试现场出发,带你一步步拆解“深度分页”的天坑,看看为什么它这么难搞,又该如何优雅应对。

**-**01-

问题定位:为什么分页越深越慢?

先别急着堆优化方案,搞清楚底层原理才是技术高手的标配!

LIMIT 不是你想的“跳过N条”

很多人以为:

SELECT * FROM orders ORDERBY id LIMIT 100000010;

真相是:

数据库真的会捞出前1000000+10条数据,然后丢掉前100万,返回你想要的那10条。

并且,如果你用了排序,还要先排序、再回表,性能开销直接爆炸:

  • 排序慢:内存排序压力大;

  • 回表慢:用了普通索引还得一条条去主键索引捞原始数据;

  • IO压力大:每一次回表 = 一次磁盘IO,10万条回表直接IO风暴。

**-**02-

单表分页如何优化?

核心思路:绕过“数着跳”的笨办法,精准命中目标数据。

方案一:子查询定位起点

SELECT * FROM user WHERE id >= (    SELECT id FROM user ORDER BY age LIMIT 1000001)LIMIT 10;
思路:先用覆盖索引快速定位第 100000 条记录的 ID,再直接查目标。

适用场景:ID 连续自增,结果集结构单一。

方案二:JOIN 联表优化回表

SELECT * FROM user t1JOIN (    SELECT id FROM user ORDER BY age LIMIT 10000010) t2 ON t1.id = t2.id;
思路:先快速找出目标 ID,再一次性拿详情,避免多次回表。

方案三:索引覆盖查询

ALTER TABLE user ADD INDEX idx_age_name(age, name);SELECT age, name FROM user ORDER BY age LIMIT 10000010;

思路:查询字段全部在索引中,无需回表!

**-**03-

分库分表翻页慢?

现在问题来了:

在分库分表之后,分页查询怎么更慢了?

假设你有 6 张分表:

SELECT * FROM orders ORDER BY create_time DESC LIMIT 100000010;

数据库会怎么执行?

每张表都查 1000010 条 × 6 张表 = 捞出 600万+ 数据
→ 汇总 → 全量排序 → 丢前100万 → 留后10条 → 你崩了

三大致命问题:

  1. 数据分散,全局排序复杂

  2. 深分页=分片全量扫描

  3. 汇总排序=内存爆炸,性能炸裂

**-**04-

三种高能方案破解“深度分页”

方案一:禁止跳页(Feed流常规操作)

核心思路:每次都从上一页最后一条数据继续往下查,不许跳页。

第一次翻页:

SELECT * FROM orders WHERE user_id = 123 ORDER BY create_time DESC LIMIT 10;

下一页查询:

SELECT * FROM orders WHERE user_id = 123 AND create_time < '2023-10-01 12:00:00' ORDER BY create_time DESC LIMIT 10;

✅ 优点:超快!每页只查索引页,0回表,不卡。

❌ 缺点:用户不能跳到第100页,适用于朋友圈、抖音等连续流场景。

方案二:二次查询法(后台系统最优解)

核心思路:拆解大分页 → 先粗查 → 再精准定位。

第一步:各分片并行查询偏移量

-- 每个分片查 offset/分片数 + limit 条数据SELECT create_time FROM orders ORDER BY create_time DESC LIMIT 16666610;

得到目标区间的最小时间戳,比如 2023-09-20 08:00:00

第二步:用这个时间戳精确获取最终数据

SELECT * FROM orders WHERE create_time >= '2023-09-20 08:00:00' ORDER BY create_time DESC LIMIT 10;

✅ 优点:支持跳页,性能远超传统深分页。

❌ 缺点:需要两次查询 + 聚合处理逻辑,稍微麻烦些。

方案三:ES + HBase 分而治之(高并发场景的终极解)

核心思路:搜索交给ES,存储交给HBase,强强联手。

流程拆解:

  1. 写入阶段:订单数据同步写入 MySQL、ES、HBase;

  2. 查询阶段

  • 第一步:用 ES 快速分页查询 ID;

  • 第二步:根据 ID 批量从 HBase 获取详情。

GET /orders/_search
{
  "query": { "match_all": {} },
  "sort": [{"create_time""desc"}],
  "from": 1000000,
  "size": 10
}

✅ 优点:

  • 支持任意跳页,百万级数据,响应只需毫秒级

  • 支持复杂搜索条件,扩展性强;

  • 无需回表,全靠 ES 倒排索引。

❌ 缺点:

  • 架构复杂,ES+HBase 成本高;

  • 数据一致性需要用 Binlog 做补偿同步。

**-**05-

面试怎么答,才能拿下 offer?

面试官想听你这样说:

“分库分表后的深度分页,核心问题是全局排序与数据分散,我们有三种主流方案:

  1. 禁止跳页:适用于 Feed 流场景,翻页快速无负担;

  2. 二次查询法:适用于后台系统,支持跳页,性能优异;

  3. ES+HBase:适用于亿级数据,毫秒响应,搜索友好。

实际项目中,我们曾在订单系统中落地过第二种方案,将分页响应时间从 15 秒降低到 300 毫秒,效果显著。”

想加分?来点骚操作:

  • 画个【分库分表 + ES/HBase】架构图,展示全流程;

  • 顺带扔出一句:“我们用 Canal 监听 Binlog 实现数据同步”;

  • 给个性能对比:“原来查 10 秒,现在查 50 毫秒”。

面试官听到这,直接 P7 起步!

总结:

分页这事,看似简单,实则很有门道。

  • 千万数据,不要用 OFFSET 跳页,纯纯自杀行为。

  • 分库分表后深度分页,是一场分布式全局排序战争。

  • 不同业务场景,要选不同方案,没有银弹,只有匹配。

热门文章

一套能保命的高并发实战指南

架构师必备:用 AI 快速生成架构图

**-**06-

粉丝福利

点点关注,送你互联网大厂面试题库,如果你正在找工作,又或者刚准备换工作。可以仔细阅读一下,或许对你有所帮助!