MySQL - 分页查询优化的两个案例解析

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: MySQL - 分页查询优化的两个案例解析

20200808090311433.png

生猛干货

带你搞定MySQL实战,轻松对应海量业务处理及高并发需求,从容应对大场面试


Table

还是我们那个老表

CREATE TABLE `employees` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',
  `age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
  `position` varchar(20) NOT NULL DEFAULT '' COMMENT '职位',
  `hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间',
  PRIMARY KEY (`id`),
  KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='员工记录表';


有个主键索引和二级联合索引 idx_name_age_position


日常场景


任何一个系统,分页查询都是必不可少的吧 ,MySQL中的分页查询 就是 limit呗 ,你有没有感觉到 越往后翻页越慢 ,常见的SQL如下

mysql> select * from employees limit 10000,10;


就是从 employees 中取出从 10001 行开始的 10 行记录。


MySQL是怎么处理这个SQL的呢?


先读取 10010 条记录,然后抛弃前 10000 条记录,仅保留10 条想要的数据 。 可想而知,如果要查询一张大表比较靠后的数据,这效率是非常低的。


那有没有优化的办法呢?


Case1 根据自增且连续的主键排序的分页查询


我们先来看一个 【根据自增且连续主键排序的分页查询】的优化案例

select * from employees limit 10000, 10

从第1万条数据开始,获取10条数据


20200808153928790.png


我们来看下执行计划

mysql> explain select * from employees limit 10000, 10 ;
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------+
|  1 | SIMPLE      | employees | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 100175 |      100 | NULL  |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------+
1 row in set
mysql> 


因为没有添加单独 order by字段,所以表示通过主键排序 。 执行计划显示全表扫描


优化

如何优化下呢?

既然是按照id排序,结合B+Tree 的特性 ,如果能从 10000这个数据位置往后扫描,是不是就会比扫描全部理论上更快一些呢?

改造如下

select * from employees where id> 10000 limit 10;

20200808154254457.png


来看下执行计划

mysql> explain  select * from employees where id> 10000 limit 10;
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+-------+----------+-------------+
| id | select_type | table     | partitions | type  | possible_keys | key     | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | employees | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL | 50087 |      100 | Using where |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+-------+----------+-------------+
1 row in set


比一比这两个,是不是下面那个更快一些



20200808154424344.png


数据可删除的场景


还有个问题,我们知道我们业务系统有些数据是可以被删除的,如果有些数据被删除了,还是按照id来排序,上面这种优化方式,会存在问题吗?

假设8888 这条业务数据被删除了

delete from employees where id = 8888 ;


那我们来看下


20200808155331287.png


20200808155401150.png


如果允许删除,那这种优化方式是不是就不正确了?


limit 10000, 10 : 就是全部数据排好序后 取第10000个开始后的10个,我们刚才删除了8888, 所以 第一条数据就变成了 10002


id> 10000 limit 10 : 这个就很好理解了,删除了8888 ,不影响 id>10000的排序 ,所以第一条数据还是 10001


适用条件


如果主键不连续,不能使用上面描述的优化方法。

如果原 SQL 是 order by 非主键的字段,按照上的方法改写会导致两条 SQL 的结果不一致。

所以这种优化方式必须同时满足以下两个条件:


  • 主键自增且连续
  • 结果是按照主键排序的

Case2 根据非主键字段排序的分页查询

来看第二个案例,实际工作中可能比第一种用的比较多

select * from employees  ORDER BY name limit 10000, 10  ;


2020080816071648.png

来看下执行计划

mysql> explain select * from employees  ORDER BY name limit 10000, 10  ;
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra          |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+
|  1 | SIMPLE      | employees | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 100175 |      100 | Using filesort |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+
1 row in set
mysql> 


按照B+Tree的结构,应该会走name字段索引,wtf , 操作的结果集太多,又要回表等等原因 , MySQL可能不选name 字段的索引 , key 字段对应的值为 null ,从而走了全表扫描 。。。。


还有 Using filesort

这部分就属于MySQL内部的优化了,可以使用Trace来追踪下MySQL是如何选择的 ,


MySQL - 使用trace工具来窥探MySQL是如何选择执行计划的


MySQL认为扫描整个索引并查找到没索引的行(可能要遍历多个索引树)的成本比扫描全表的成本更高,所以优化器放弃使用索引。


那既然知道不走索引的原因,那么怎么优化呢?


关键是让排序时返回的字段尽可能少,所以可以让排序和分页操作先查出主键,然后根据主键查到对应的记录.


让排序时返回的字段尽可能少–》 只返回id , 然后用返回的特定范围的id ,再和原表关联,只取特定范围内的数据 ,肯定比全表扫描要快。

改造如下


 select * from employees a inner join (select id from employees order by name limit 10000,10) b on a.id = b.id;


先找到id (select id 使用覆盖索引),然后用这个结果集 (这个案例中就只有10条结果)去和 employees 关联

看看执行计划


2020080816320129.png

原 SQL 使用的是 filesort 排序,优化后的 SQL 使用的是索引排序。

当然了,结果集也是和优化前是一致的


20200808162926852.png


搞定MySQL

https://artisan.blog.csdn.net/article/details/107874528?spm=1001.2014.3001.5502

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
27天前
|
SQL 关系型数据库 MySQL
深入解析MySQL的EXPLAIN:指标详解与索引优化
MySQL 中的 `EXPLAIN` 语句用于分析和优化 SQL 查询,帮助你了解查询优化器的执行计划。本文详细介绍了 `EXPLAIN` 输出的各项指标,如 `id`、`select_type`、`table`、`type`、`key` 等,并提供了如何利用这些指标优化索引结构和 SQL 语句的具体方法。通过实战案例,展示了如何通过创建合适索引和调整查询语句来提升查询性能。
131 9
|
6天前
|
SQL 关系型数据库 MySQL
MySQL派生表合并优化的原理和实现
通过本文的详细介绍,希望能帮助您理解和实现MySQL中派生表合并优化,提高数据库查询性能。
35 16
|
7天前
|
SQL 关系型数据库 MySQL
MySQL派生表合并优化的原理和实现
通过本文的详细介绍,希望能帮助您理解和实现MySQL中派生表合并优化,提高数据库查询性能。
24 7
|
21天前
|
存储 关系型数据库 MySQL
10个案例告诉你mysql不使用子查询的原因
大家好,我是V哥。上周与朋友讨论数据库子查询问题,深受启发。为此,我整理了10个案例,详细说明如何通过优化子查询提升MySQL性能。主要问题包括性能瓶颈、索引失效、查询优化器复杂度及数据传输开销等。解决方案涵盖使用EXISTS、JOIN、IN操作符、窗口函数、临时表及索引优化等。希望通过这些案例,帮助大家在实际开发中选择更高效的查询方式,提升系统性能。关注V哥,一起探讨技术,欢迎点赞支持!
117 5
|
30天前
|
缓存 关系型数据库 MySQL
MySQL 索引优化以及慢查询优化
通过本文的介绍,希望您能够深入理解MySQL索引优化和慢查询优化的方法,并在实际应用中灵活运用这些技术,提升数据库的整体性能。
49 7
|
29天前
|
存储 关系型数据库 MySQL
double ,FLOAT还是double(m,n)--深入解析MySQL数据库中双精度浮点数的使用
本文探讨了在MySQL中使用`float`和`double`时指定精度和刻度的影响。对于`float`,指定精度会影响存储大小:0-23位使用4字节单精度存储,24-53位使用8字节双精度存储。而对于`double`,指定精度和刻度对存储空间没有影响,但可以限制数值的输入范围,提高数据的规范性和业务意义。从性能角度看,`float`和`double`的区别不大,但在存储空间和数据输入方面,指定精度和刻度有助于优化和约束。
120 5
|
29天前
|
缓存 关系型数据库 MySQL
MySQL 索引优化与慢查询优化:原理与实践
通过本文的介绍,希望您能够深入理解MySQL索引优化与慢查询优化的原理和实践方法,并在实际项目中灵活运用这些技术,提升数据库的整体性能。
80 5
|
2月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
103 2
|
20天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
20天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析