当前位置: 首页 > news >正文

大数据量的分页,怎么办?

背景

分页功能这个是大家工作中经常碰到的,相信大家脑海中直接跳出limit offset,size . 当进行深度分页时会卡崩,为什么?因为执行这条sql需要先读取offset+size条数据,根据实际业务在需要进行排序,然后丢弃到offset条数据。

例如:假设有一张表,三个字段,分别为id,name,age. 其中id是主键。age 普通索引。

需求:我们需要查找出按照年龄排序offset=1000000  size =10 的记录。

如果使用limit offset,size,需要进行offset+size回表操作。直接卡崩你~

不同公司的存储架构不同,需针对单表和分库分表来考虑。

一、单表

1.1  覆盖索引

保证你所查询的字段能通过索引来查询出来。

例如:

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

优点:减少了 offset+size 次回表操作。

1.2  子分页查询

-- 先查索引定位ID,再查数据
SELECT * FROM user 
WHERE id >= (SELECT id FROM user ORDER BY age LIMIT 100000, 1)
LIMIT 10;

原理:通过主键索引提前查询到offsite的位置,在通过这个进行分页,减少了offsite次回表操作。

缺点:id 必须是连续递增的;不适合过滤条件筛选

1.3 游标分页

-- 第一页
SELECT * FROM user ORDER BY age LIMIT 10;-- 获取下一页(假设上一页最后一条记录的id是12345)
SELECT * FROM user
WHERE id > 12345 
ORDER BY age 
LIMIT 10;

优点:避免了OFFSET带来的性能问题,适合无限滚动场景

缺点:禁止跳页

1.4 分区表

这个是根据自己的业务来实现,适合时间或者id范围进行分区。

CREATE TABLE user(id INT NOT NULL,age int NOT NULL,name char(100)
) PARTITION BY RANGE (age) (PARTITION p10 VALUES LESS THAN (11),PARTITION p20 VALUES LESS THAN (21),PARTITION p30 VALUES LESS THAN (31),PARTITION pmax VALUES LESS THAN MAXVALUE
);

优点

  • 减少单个分区数据量,提高查询效率

  • 可以并行查询不同分区

  • 便于维护(可单独备份/恢复分区)

缺点

  • 所有分区仍在同一数据库实例

  • 分区键选择不当可能导致性能下降

  • 某些操作(如跨分区JOIN)可能更慢

二、分库分表

假设分为5个库,每个库有2张表,按照用户id来分片。

问题分析痛点:

  • 1:数据分散,全局排序难
    各分片数据独立排序,合并后可能乱序,必须全量捞数据重排。
  • 2:深分页=分片全量扫描
    每张表都要查 offset + size 条数据,性能随分片数量指数级下降。
  • 3:内存归并压力大
    100万条数据 × 10个分片 = 1000万条数据在内存排序,非常可能触发oom。

1.1 游标分页

-- 第一页
SELECT * FROM user ORDER BY age LIMIT 10;-- 获取下一页(假设上一页最后一条记录的id是12345)
SELECT * FROM user
WHERE id > 12345 
ORDER BY age 
LIMIT 10;

缺点:禁止用户跳页

1.2 二次查找法

-- 第一次查询(查询的offset=准备查询的偏移量/片键)
SELECT id FROM user ORDER BY age LIMIT 1000000/10,10;-- 第二次查询(假设第一次查询的第一条记录的id是12345)
SELECT * FROM user
WHERE id > 12345 
ORDER BY age 
LIMIT 10;

原理: 第一次查找出所有分片上的排序数据。第二查询根据分片上查询的结果进行分页上全局最小的数据进行查询。

优点:避免全局数据排序;支持跳页

缺点:两次查询,麻烦

1.3  结合ES进行查询

ES 适合复杂的查询及其分页(倒排索引)。

#1,在es中查找出符合的id
GET /user/_search  
{  "query": { "match_all": {} },  "sort": [{"create_time": "desc"}],  "from": 1000000,  "size": 10  
}  
# 第二次查询(假设第一次查询的id是1,2,3,4,5,6.。。。。10)
SELECT * FROM user
WHERE id in (1,2,3,4,5,6,7,89,10) 
ORDER BY age 

优点:

  • 大数据量情况性能秒杀数据库,百万数据毫秒响应
  • 支持复杂的搜索条件(ES擅长之处)

缺点:

  • 需要额外引入es组件,架构复杂度搞
  • 数据一致性问题

题外话:如何保证Mysql+ES的数据一致性问题

  • 双写模式
// 伪代码示例
public void createUser(User user) {// 1. 分库分表写入int shard = user.getId() % SHARD_COUNT;userDao.insert(shard, user);// 2. ES写入esClient.index(user.toDocument());// 3. 可以考虑加入本地事务表保证一致性
}
  • 基于CDC的异步同步方案
  1. 使用Canal/Debezium/Maxwell 监听MySQL binlog

  2. 通过Kafka消息队列解耦

  3. 最终写入Elasticsearch

相关文章:

  • MagnTek MT6816-ACD 一款基于各向异性磁阻(AMR)技术的磁性角度传感器 IC
  • 现代浏览器剪贴板操作指南 + 示例页面 navigator.clipboard 详解与实战
  • 1.20.1 服务器系统(windows,Rocky 和 Ubuntu )体验
  • 浅议 3D 展示技术为线上车展新体验带来的助力​
  • 【Docker基础】Docker镜像管理:docker rmi、prune详解
  • 基于Spring Boot瀚森健身房会员管理系统设计与实现【源码+文档】
  • React JSX语法
  • 基于YOLO的智能车辆检测与记录系统
  • Vue.js 粒子连线动画组件 - FlyingLines
  • 【RAG优化】深度解析开源项目MinerU:从PDF解析到多模态理解的工业级解决方案
  • 《Whisper:开启语音识别新时代的钥匙》
  • 什么是跨域问题?后端如何解决跨域问题?
  • JVM(面试)
  • 【C/C++】Gtest + GMock 简单使用示例
  • 简说MQTT
  • 【分布式理论】读确认数与写确认数:分布式一致性的核心概念
  • C# 将 Enum枚举转成List,并显示在下拉列表中
  • 如何开发HarmonyOS 5的分布式通信功能?
  • 动态ds-vnp之normal和shortcut两种方式配置案例
  • 【软考高级系统架构论文】论企业应用系统的分层架构风格
  • 创建一个网站需要多少钱/专业关键词优化平台
  • 深圳软件开发公司在哪里/淮南网站seo
  • 移除wordpress4版本号/seo推广编辑
  • 武汉科技职业学院有哪些专业/seo教学培训
  • 学院 网站 两学一做/seo管理工具
  • wordpress 批量修改分类/seo视频教学网站