想象你有一个图书馆(单库单表),所有书按顺序放在书架上。当你要找第100-110本书时,直接数到第100本就能拿到。但图书馆的书爆炸式增长后,馆长决定:
-
分库:把书分到10个房间(10个数据库)
-
分表:每个房间再分成20个书架(20张表)
每个书架只放特定规则的书(比如按ID取模:ID % 200
)
问题来了:
当用户要求「按时间倒序排列,显示第1000-1010条数据」时:
-
每个房间的书架都是独立排序的
-
无法直接知道全局第1000条数据在哪里
解决方案:
想象你管理10个快递分拣站(分库分表),每个站点有自己的包裹编号。现在要找出全国第1000-1010个发出的包裹:
下面具体说下各个方案、优缺点和做法:
方案 |
适用场景 |
技术实现难度 |
缺点 |
全局排序法 |
数据量小,分片少 |
低 |
性能差,深分页爆炸 |
二次查询法 |
排序字段有索引,分片较多 |
中 |
需要两次查询 |
游标分页法 |
有全局有序字段(如ID、时间戳) |
低 |
不支持随机跳页 |
搜索引擎辅助 |
高频复杂查询,容忍秒级延迟 |
高 |
维护成本高,数据延迟
|
(1)全局排序法(简单但低效)
实现步骤:
-
每个分片独立执行ORDER BY create_time DESC LIMIT 0, 1010
-
把所有分片的结果汇总到一个中心节点
-
中心节点对所有数据进行全局排序,再取第1000-1010条
缺点:
(2)二次查询法(优化性能)
核心思路:避免全量数据传输,分两次查询缩小范围
步骤示例(查询第1000-1010条):
-
第一次查询:每个分片返回前1010条数据的最小时间戳
-
取所有分片中第1010小的全局时间戳T_global
-
第二次查询:各分片查询create_time >= T_global
的数据
-
汇总后再次排序取最终结果
优点:
-
大幅减少数据传输量
-
适合排序字段有索引的情况(如时间戳)
(3)游标分页法(避免深分页)
核心思路:用连续且唯一的字段(如自增ID、时间戳)作为游标
优势:
-
完全避免offset
带来的性能问题
-
天然适合分库分表,每个分片只需按游标条件过滤
限制:
-
必须有一个全局有序且唯一的字段
-
用户无法直接跳转到任意页码(如第1000页)
(4)搜索引擎辅助(终极武器)
适用场景:高频复杂分页查询
实现方式:
-
将分库分表的数据实时同步到Elasticsearch/Solr
-
所有分页查询直接走搜索引擎
-
搜索引擎内部维护全局排序
优势:
-
完全屏蔽分库分表复杂性
-
支持复杂条件过滤+高并发查询
代价:
实际开发中的建议
-
优先使用游标分页:
-
限制最大分页深度:
-
结合业务优化:
-
终极方案: