MySQL深分页问题原理与三种解决方案
作者:JAVA前线 发布时间:2024-01-27 14:12:34
标签:MySQL,深分页,原理,解决
1 深分页问题
1.1 创建表
CREATE TABLE `player` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`player_id` varchar(256) NOT NULL COMMENT '运动员编号',
`player_name` varchar(256) NOT NULL COMMENT '运动员名称',
`height` int(11) NOT NULL COMMENT '身高',
`weight` int(11) NOT NULL COMMENT '体重',
`game_performance` text COMMENT '最近一场比赛表现',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
1.2 新增100万条数据
@SpringBootTest(classes = TestApplication.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class PlayerServiceTest {
@Resource
private PlayerRepository playerRepository;
@Test
public void initBigData() {
for (int i = 0; i < 1000000; i++) {
PlayerEntity entity = new PlayerEntity();
entity.setPlayerId(UUID.randomUUID().toString());
entity.setPlayerName("球员_" + System.currentTimeMillis());
entity.setWeight(150);
entity.setHeight(188);
entity.setGamePerformance("{\"runDistance\":8900.0,\"passSuccess\":80.12,\"scoreNum\":3}");
playerRepository.insert(entity);
}
}
}
1.3 深分页语句
select * from player limit 990000,5
1.4 结果分析
查询耗时:1.233秒
本语句目标查询
[990001-990005]
五条数据但是执行时需要排序
[1-990005]
数据最终丢弃
[1-990000]
只返回[990001-990005]
数据
2 深分页优化方案
2.1 方案一
我们可以从业务形态维度去解决,可以参考搜索引擎解决方案。因为ES也存在深分页问题,搜索引擎解决方案是在业务上会限制查询页数。因为页数越大,内容相关度越低,所以页数太大对业务价值不高。MySQL可以类比处理:
限制查询页数
限制全量导出
查询时要求带必要条件(时间范围、userId)
2.2 方案二
2.2.1 优化语句
select * from player a, (select id as tmpId from player limit 990000,5) b WHERE a.id = b.tmpId
2.2.2 执行计划
(1) 查看计划
explain select * from player a, (select id as tmpId from player limit 990000,5) b WHERE a.id = b.tmpId
(2) 执行顺序
id越大执行顺序越靠前
id相同则按照行数从上到下执行
本语句执行顺序如下图:
第一步和第二步表示执行子查询
第三步表示player表与子查询关联
(3) explain type
访问类型是重要分析指标:
(4) explain Extra
Extra表示执行计划扩展信息重点关注三个:
2.2.3 结果分析
查询耗时:0.5秒
原因是覆盖索引提升分页查询效率(只查询ID列)
覆盖索引含义是查询时索引列完全包含查询列
using index表示使用覆盖索引,性能提升
2.3 方案三
2.3.1 优化语句
select * from player where id > 990000 LIMIT 5
2.3.2 执行计划
(1) 查看计划
explain select * from player where id > 990000 LIMIT 5
(2) 结果分析
查询耗时:0.001秒
range表示索引范围搜索性能尚可
(3) 适用场景
不适用跳页场景
只适用【上一页】【下一页】场景
3 MyBatis
<mapper namespace="com.test.java.front.test.mysql.deep.page.repository.PlayerRepository">
<resultMap id="BaseResultMap" type="com.test.java.front.test.mysql.deep.page.entity.PlayerEntity">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="player_id" jdbcType="VARCHAR" property="playerId" />
<result column="player_name" jdbcType="VARCHAR" property="playerName" />
<result column="height" jdbcType="INTEGER" property="height" />
<result column="weight" jdbcType="INTEGER" property="weight" />
<result column="game_performance" jdbcType="LONGVARCHAR" property="gamePerformance" />
</resultMap>
<sql id="Base_Column_List">
id, player_id, player_name, height, weight, game_performance
</sql>
<sql id="conditions">
<where>
<if test="playerId != null">
and player_id = #{playerId,jdbcType=VARCHAR}
</if>
</where>
</sql>
<sql id="pager">
<if test="skip != null and limit != null">
limit #{skip}, #{limit}
</if>
</sql>
<!-- 查询条数 -->
<select id="selectPageCount" parameterType="com.test.java.front.test.mysql.deep.page.param.biz.PlayerQueryParam" resultType="java.lang.Long">
select count(*) from player
<include refid="conditions" />
</select>
<!-- 分页方式1:普通分页存在深分页问题 -->
<!-- select * from player limit 990000,5 -->
<select id="selectPager1" parameterType="com.test.java.front.test.mysql.deep.page.param.biz.PlayerQueryParam" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from player
<include refid="conditions" />
<include refid="pager" />
</select>
<!-- 分页方式2:覆盖索引优化深分页问题 -->
<!-- select * from player a, (select id as tmpId from player limit 990000,5) b where a.id = b.tmpId -->
<select id="selectPager2" parameterType="com.test.java.front.test.mysql.deep.page.param.biz.PlayerQueryParam" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from player a,
(
select id as tmpId from player
<include refid="conditions" />
<include refid="pager" />
) b
where a.id = b.tmpId
</select>
<!-- 分页方式3:Id分页不支持跳页 -->
<!-- select * from player where id > 990000 limit 5 -->
<select id="selectPager3" parameterType="com.test.java.front.test.mysql.deep.page.param.biz.PlayerQueryIdParam" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
<include refid="conditions" />
from player where id > #{startId} limit #{pageSize}
</select>
</mapper>
4 文章总结
本文第一介绍深分页问题表现和原因。第二介绍深分页问题三种解决方法,方案一是从业务维度优化,方案二是使用覆盖索引进行优化,方案三是使用Id分页。第三展示MyBatis相关代码。
来源:https://juejin.cn/post/7228801593327009829


猜你喜欢
- 扫雷是一款益智类小游戏,最早于 1992 年由微软在 Windows 上发行,游戏适合于全年龄段,规则简单,即在最短的时间内找出所有非雷格子
- 我就废话不多说了,大家还是直接看代码吧~import torch.nn as nnimport torch.nn.functional as
- 前言上篇我们介绍了微前端实现沙箱的几种方式,没看过的可以下看下JS沙箱这篇内容,扫盲一下。接下来我们通过源 码详细分析下qiankun沙箱实
- 在pycharm使用过程中,对于每次新建文件的编码格式和关于代码编写者的一些个人信息快捷填写,方法如下:1.打开pycharm,选择File
- 这篇文章主要介绍了pandas和spark dataframe互相转换实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定
- 本文主要跟大家分享在类unix操作系统下supervisor的使用以及一些关于进程的知识一、问题背景1、背景如何才能让一个进程摆脱终端,获得
- 本文主要是用PyTorch来实现一个简单的回归任务。 编辑器:spyder1.引入相应的包及生成伪数据import torchimport
- Appium在前文的讲解中,我们学会了如何安装Appium,以及一些基础获取App元素内容的方式。但认真看过前文的读者,肯定在博主获取元素的
- 糟糕的SQL查询语句可对整个应用程序的运行产生严重的影响,其不仅消耗掉更多的数据库时间,且它将对其他应用组件产生影响。如同其它学科,优化查询
- 一、前言实现名片管理系统,首先要创建两个python file ,分别是cards_main.py和cards_tool.py,前一个是主代
- 在Windows平台上, js可以调用很多Windows提供的ActivexObject,本文就使用js来实现文档处理, 和使用js编写Ac
- Mysql数据库备份和还原常用的命令是进行Mysql数据库备份和还原的关键,没有命令,什么都无从做起,更谈不上什么备份还原,只有给系统这个命
- Golang 开发者遇到的许多问题之一是尝试将一个函数的参数设置为可选. 这是一个非常常见的用例, 有些对象应该使用一些基本的默认设置来开箱
- 目录分页后添加删除功能实现模态框编辑内容完整代码笔记利用layui框架实现分页:layui实现完整表格分页:自己实现分页:# name: m
- 主键的生成方式主要有三种: 一. 数据库自动生成 二. GUID 三. 开发创建 严格讲这三种产生方式有一定的交叉点,其定位方式将在下面进行
- Internet Explorer 8 Beta 测试了一年多之后,今天,IE8 终于发布了。它绝对好过 IE7,还有一些不错的新功能,如
- 本文实例为大家分享了python实现递归查找某个路径下所有文件中的中文字符,供大家参考,具体内容如下# -*- coding: utf-8
- 概述pandas中与库版本或依赖库版本相关的API主要有以下4个:pandas.__version__:查看pandas简要版本信息。pan
- 数据库中提取长长的文章,总是有碍网页的排版布局。所以,想固定地提取一部分字符,然后后面有……替代。相关推荐:当标题不能显示完整的时候 1、原
- 目录原理先行实践篇完整代码原理先行作为一个资深的小说爱好者,国内很多小说网站如出一辙,什么 🖊*阁啊等等,大都是 get 请求返回 html