分库跨库分页需重建全局排序视野,业务层可用全量拉取合并、游标分页或索引表;中间件如shardingsphere通过sql拆解与归并排序实现透明分页,但不支持跨库join等复杂查询。

分库之后,跨库分页不能直接用 ORDER BY time OFFSET X LIMIT Y,因为每个库只掌握局部数据,按时间排序后取第 N 页,结果既不连续也不准确。解决的核心是:**重建全局排序视野**。下面从业务层和中间件两个路径讲清楚怎么做、为什么这么选、关键注意点在哪。
业务层实现:自己拼全局结果
适合中小规模、对精度要求高、又不想引入新组件的场景。本质是把“数据库该干的活”,挪到应用代码里做。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
全量拉取 + 内存合并排序:对每个分库执行
SELECT * FROM t_user ORDER BY time DESC LIMIT (X+Y)(比如查第3页、每页10条,就各库取前40条),拿到所有候选数据后,在 Java/Go 等服务中按 time 合并、去重、全局排序,再截取第31–40条返回。优点是结果100%准确;缺点是网络传输量大、内存压力随分库数和页码增长,第100页可能要拉几万条才够。 -
游标分页 + 状态缓存:放弃传统页码,改用“上次最后一条的 time 和 id”作为下一页凭证。例如上一页最后一条是
time=2026-08-28 10:30:00, id=9999,下一页查每个库:WHERE time ,再合并取前10条。这种方式规避了 offset 跳过问题,性能稳定,但前端需改造分页交互逻辑。 -
索引表辅助查询:单独建一张轻量级全局索引表
t_user_time_index(uid, time, db_idx),写入用户时同步更新。查第3页时,先查索引表:SELECT uid FROM t_user_time_index ORDER BY time DESC LIMIT 20,10,再根据 uid 和 db_idx 去对应库回查完整记录。省去了全量扫描,但多了一次写入开销和索引表一致性维护成本。
中间件实现:让代理层兜底
这是目前主流生产环境首选,尤其当分库数量多、SQL 复杂度高、团队缺乏深度数据库治理能力时。
-
SQL 解析与路由重写:ShardingSphere、MyCat 等中间件收到
SELECT * FROM t_user ORDER BY time LIMIT 100,20后,会自动拆成 N 条带LIMIT 0,120的语句下发到各分库;收集全部结果后,在内存中归并排序(类似归并排序算法),再取第101–120条返回给应用。整个过程对业务透明,无需改 SQL 或代码。 - 聚合下推优化:高级中间件支持将部分计算下推,比如 COUNT、MAX、GROUP BY,但分页本身无法下推——必须收全数据才能定序。所以它本质上仍是“业务层全量拉取”的封装升级版,只是把合并逻辑从你代码里移到了中间件进程里。
-
注意中间件的局限:不支持跨库 JOIN、子查询嵌套过深、或非分片键的 WHERE 条件(如
WHERE name='张三')会导致广播查询,性能骤降;另外,COUNT(*)分页总数统计也得走全库扫描,建议用近似值或异步统计替代。
选型建议:看规模、精度和演进节奏
小团队起步期,优先用游标分页,零新增组件、性能稳、易验证;中大型系统且已有中间件基建,直接走 ShardingSphere,开发效率高;如果必须精确总数+任意页码跳转+数据量极大,就得接受索引表方案,用空间换时间,并搭配定时校验保障一致性。










