mysql分页查询关键_MySQL优化教程之超大分页查询

背景

基本上只要是做后台开发,都会接触到分页这个需求或者功能吧。基本上大家都是会用MySQL的LIMIT来处理,而且我现在负责的项目也是这样写的。但是一旦数据量起来了,其实LIMIT的效率会极其的低,这一篇文章就来讲一下LIMIT子句优化的。

LIMIT优化

很多业务场景都需要用到分页这个功能,基本上都是用LIMIT来实现。

建表并且插入200万条数据:

# 新建一张t5表

CREATE TABLE `t5` (

`id` int NOT NULL AUTO_INCREMENT,

`name` varchar(50) NOT NULL,

`text` varchar(100) NOT NULL,

PRIMARY KEY (`id`),

KEY `ix_name` (`name`),

KEY `ix_test` (`text`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

# 创建存储过程插入200万数据

CREATE PROCEDURE t5_insert_200w()

BEGIN

DECLARE i INT;

SET i=1000000;

WHILE i<=3000000 DO

INSERT INTO t5(`name`,text) VALUES('god-jiang666',concat('text', i));

SET i=i+1;

END WHILE;

END;

# 调用存储过程插入200万数据

call t5_insert_200w();

在翻页比较少的情况下,LIMIT是不会出现任何性能上的问题的。

但是如果用户需要查到最后面的页数呢?

通常情况下,我们要保证所有的页面可以正常跳转,因为不会使用order by xxx desc这样的倒序SQL来查询后面的页数,而是采用正序顺序来做分页查询:

select * from t5 order by text limit 100000, 10;

73b22209562edda340c085544ce8f0fe.png

采用这种SQL查询分页的话,从200万数据中取出这10行数据的代价是非常大的,需要先排序查出前1000010条记录,然后抛弃前面1000000条。我的macbook pro跑出来花了5.578秒。

接下来我们来看一下,上面这条SQL语句的执行计划:

explain select * from t5 order by text limit 1000000, 10;

a57b88672a6b44d8a963322c45544b5d.png

从执行计划可以看出,在大分页的情况下,MySQL没有走索引扫描,即使text字段我已经加上了索引。

这是为什么呢?

回到MySQL索引(二)如何设计索引中有提及到,MySQL数据库的查询优化器是采用了基于代价的,而查询代价的估算是基于CPU代价和IO代价。

如果MySQL在查询代价估算中,认为全表扫描方式比走索引扫描的方式效率更高的话,就会放弃索引,直接全表扫描。

这就是为什么在大分页的SQL查询中,明明给该字段加了索引,但是MySQL却走了全表扫描的原因。

然后我们继续用上面的查询SQL来验证我的猜想:

explain select * from t5 order by text limit 7774, 10;

6fa7f5496952ea16b608fe8d16cf8b26.png

explain select * from t5 order by text limit 7775, 10;

6f2fa8b1f516a15e24b2d68aacd9d607.png

以上的实验均在我的mbp上运行的,在7774这个临界点上,MySQL分别采用了索引扫描和全表扫描的查询优化方式。

所以可以认为MySQL会根据它自己的代价查询优化器来判断是否使用索引。

由于MySQL的查询优化器的算法核心是我们无法人工干预的,所以我们的优化思路就要着手于如何让分页维持在最佳的的分页临界点。

优化方式

1、使用覆盖索引

如果一条SQL语句,通过索引可以直接获取查询的结果,不再需要回表查询,就称这个索引为覆盖索引。

在MySQL数据库中使用explain关键字查看执行计划,如果extra这一列显示Using index,就表示这条SQL语句使用了覆盖索引。

让我们来对比一下使用了覆盖索引,性能会提升多少吧。

# 没有使用覆盖索引

select * from t5 order by text limit 1000000, 10;

963ba371e7c45ea77925d71c2be26e0c.png

2c3aa6528e9954dc5bcc8bb3dfbc2ef6.png

这次查询花了3.690秒,让我们看一下使用了覆盖索引优化会提升多少性能吧。

# 使用了覆盖索引

select id, `text` from t5 order by text limit 1000000, 10;

9e3b1b637feb7e155261cc2b4bc6781b.png

f72c7ea9be1fe26df7735f6b773786d4.png

从上面的对比中,超大分页查询中,使用了覆盖索引之后,花了0.201秒,而没有使用覆盖索引花了3.690秒,提高了18倍多,这在实际开发中,就是一个大的性能优化了。(该数据在我的mbp上运行得出)

2、子查询优化

因为实际开发中,用SELECT查询一两列操作是非常少的,因此上述的覆盖索引的适用范围就比较有限。

所以我们可以通过把分页的SQL语句改写成子查询的方法获得性能上的提升。

select * from t5 where id>=(select id from t5 order by text limit 1000000, 1) limit 10;

36e12ab40b61e08ce9a00862186f5a83.png

其实使用这种方法,提升的效率和上面使用了覆盖索引基本一致。

但是这种优化方法也有局限性:

这种写法,要求主键ID必须是连续的

Where子句不允许再添加其他条件

3、延迟关联

和上述的子查询做法类似,我们可以使用JOIN,先在索引列上完成分页操作,然后再回表获取所需要的列。

select a.* from t5 a inner join (select id from t5 order by text limit 1000000, 10) b on a.id=b.id;

e210992b1f3eb780947eccde90bff68f.png

从实验中可以得出,在采用JOIN改写后,上面的两个局限性都已经解除了,而且SQL的执行效率也没有损失。

4、记录上次查询结束的位置

和上面使用的方法都不同,记录上次结束位置优化思路是使用某种变量记录上一次数据的位置,下次分页时直接从这个变量的位置开始扫描,从而避免MySQL扫描大量的数据再抛弃的操作。

select * from t5 where id>=1000000 limit 10;

f0dd6a10ad580ee43021462e3e4febf8.png

根据以上实验,不难得出,由于使用了主键索引做分页操作,SQL的性能是最快的。

总结

介绍了超大分页查询性能过差的原因,还有分享了几个优化思路

超大分页的优化思路就是让分页的SQL尽量在最佳的性能区间执行,不要触发全表扫描即可

希望以上的分享,可以让你们在MySQL这条路上少走弯路~~~

参考资料

《MySQL性能优化》第六章 查询优化性能

《数据库查询优化器的艺术》

到此这篇关于MySQL优化教程之超大分页查询的文章就介绍到这了,更多相关MySQL超大分页查询内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/529554.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql r_R之RMySQL

linux&#xff0c;mysql和R的版本信息&#xff1a;Linux naci 3.19.0-16-generic #16-Ubuntu SMPServer version: 5.6.24-0ubuntu2 (Ubuntu)R version 3.1.2 (2014-10-31) -- "Pumpkin Helmet"mysql的linux安装&#xff0c;参照上一篇关于liunx下安装mysql的文章。1.…

mysql 字符转换函数是_MySQL日期和字符串转换函数

Mysql中to_char()和str_to_date()函数转载路径&#xff1a;https://blog.csdn.net/ricardo_mli/article/details/802175121.字符串转换成日期格式str_to_date(date,’%Y-%m-%d’)----->相当于Oracle中的to_char();例子&#xff1a;INSERT INTOt_order(order_time)VALUES(str…

mysql数据加百分号_使用MySQL SELECT语句时,在每个值的末尾添加一个百分号(%)...

要在末尾添加百分号&#xff0c;请使用CONCAT()函数。让我们首先创建一个表-mysql> create table DemoTable(StudentId int NOT NULL AUTO_INCREMENT PRIMARY KEY,StudentName varchar(100),StudentScore int);使用插入命令在表中插入一些记录-mysql> insert into DemoTa…

mysql 分页 jdbc_JDBC调用MySQL分页存储过程实现(一)

DROP PROCEDURE IF EXISTS pro_pager;CREATE DEFINER root% PROCEDURE pro_pager(in p_pageNo int, /*当前页*/in p_perPageCnt int, /*每页记录数*/in p_sql VARCHAR(2000), /*查询sql语句*/out v_totalRowsCnt int, /*记录总条数*/out v_totalPageCnt int) /*记录总页数*/BE…

mariadb mysql表_mysql/mariadb学习记录——创建删除数据库、表的基本命令

查看已有的数据库&#xff1a;mysql>show databases;--------------------| Database |--------------------| information_schema || test |--------------------2 rows in set (0.05 sec)新建数据库语句:mysql>create database wzu;Query OK,1 row affected (0.05sec)m…

zabbix proxy mysql_zabbix proxy 配置

在监控大量服务器时&#xff0c;如果将所有的请求都发送到一个zabbix server上&#xff0c;将会对我们的zabbix server造成很大的压力&#xff0c;我们在规划多个区域或机房进行监控的时候&#xff0c;会考虑到使用zabbix proxy 来代理zabbix server 的部分功能。zabbix server…

mysql修改数据库结构用哪一项_mysql alter修改数据库表结构用法

1.alter操作表字段(1)增加字段alter table 表名 add 字段名 字段类型&#xff1b;alter table student add name varchar(10)&#xff1b;(2)修改字段alter table 表名 change 旧字段名 新字段名 字段类型&#xff1b;alter table 表名 modify 字段名 字段类型&#xff1b;//修…

mvc mysql linq_MVC3+Linq to sql 显示数据库中数据表的数据

1&#xff1a;首先创建asp.net mvc3应用程序 2&#xff1a;创建项目完成后 找到controllers文件鼠标右击选择添加控制器 3 为models文件夹添加一个linq to sql类文件&#xff0c;然后把数据库中的数据库复制进来。如截图操作 4&#xff1a;添加控制器好后会生成一个HomeControl…

bash给脚本加进度条_shell脚本实现多彩进度条

代码如下&#xff1a;1 #!/bin/bash2 i0;3 str""4 arr("|" "/" "-" "\\")5 while [ $i -le 100 ]6 do7 let indexi%48 let indexcolori%89 let color30indexcolor10 printf "\e[0;$color;1m[%-100s][%d%%]%c\r" &…

koa mysql mongodb_koa 操作MongoDB数据库

安装安装MongoDBnpm install mongodb --save引入中间件引入mongodb下面的连接模块MongoClient// 引入MongoDB 连接模块const MongoClient MongoDB.MongoClient;配置中间件定义数据库连接的地址以及配置数据库的名称let url "mongodb://localhost:27017/";let dbNam…

mysql 64位 安装1045_MySql 安装时的1045错误

MySql 安装到最后一步遇到1045错误Access denied for user rootlocalhost (usingpassword:YES)解决方案一&#xff1a;卸载MySQL&#xff0c;重新安装1, 卸载MySQL2, 删除目录 C:\Documents and Settings\All Users\ApplicationData\MySQL,还要删除MySQL安装目录3, 重新安装MyS…

mysql工作表格制作教程_Access制作复杂报表

何制作复杂报表利用excel输出复杂报表 在读这篇文章以前首先要提醒大家&#xff0c;Access 本身的报表也具有很强的实用性和强大的功能&#xff0c;只有当你发掘了其本身全部的功能却仍不能满足你对报表的特殊要求时才请使用 Excel 输出报表。很明显&#xff0c;使用 Excel 输出…

php+mysql投票代码_PHP+jQuery+MySql实现红蓝投票功能

本文是一篇综合知识应用类文章&#xff0c;需要您具备PHP、jQuery、MySQL以及html和css方面的基本知识。本文在《PHPMySqljQuery实现的“顶”和“踩”投票功能》一文基础上做了适当改进&#xff0c;共用了数据表&#xff0c;您可以先点击了解这篇文章。HTML我们需要在页面中展示…

numpy 最大值_第 85 天:NumPy 统计函数

数学统计在我们的程序当中特别是数据分析当中是必不可少的一部分&#xff0c;本文就来介绍一下 NumPy 常见的统计函数。最大值与最小值numpy.amin()用于计算数组中的元素沿指定轴的最小值。可以通过 axis 参数传入坐标轴来指定统计的轴&#xff0c;当指定 axis 时&#xff0c;a…

java中如何实现变量可配置_Java基础-如何配置环境变量

Java环境变量详细教程第一步、打开电脑环境变量设置窗口以Win10系统为例子。在桌面找到此电脑&#xff0c;右键此电脑— —>属性&#xff0c;点击属性— —>点击左侧高级系统设置点击高级系统设置点击环境变量第二步、新建JAVA_HOME点击系统变量中的新建,出现输入框&…

python三引号解析_[宜配屋]听图阁

和C语言一样&#xff0c;引号属于特殊功能字符&#xff0c;不能够像普通字符那样直接通过print打印&#xff0c;需要进行一些处理&#xff0c;比如说反斜杠转义等。这里介绍几种打印三引号的方法&#xff0c;希望对需要的朋友有用。1、第一中方法比较简单&#xff0c;直接使用三…

abaqus python 建立节点集合_在Python中创建Abaqus集

我想用Python在Abaqus中创建一个带边的几何集。我不会事先知道边的数目。尝试将边放入数组中&#xff0c;然后创建集合。你知道吗myEdgesForSet []for i in range(0, len(mdb.models[Model].parts[Part].edges)):if something in mdb.models[Model].parts[Part].edges[i].feat…

java类默认访问权限_Java的四种访问权限

❝ 所谓访问权限&#xff0c;指的就是本类中的成员变量、成员方法对其他类的可见性❞试想一想&#xff0c;当我们修改一个非常庞大的项目时&#xff0c;如果所有变量和方法都是公共权限&#xff0c;那么后端中任何类都有权限去修改它的变量和方法&#xff0c;很有可能修改后就导…

java 数组 反射_【译】10. Java反射——数组

用Java反射来处理数组有时候是技巧性很强的。特别是如果你需要获取一个给定类型的数组的Class对象&#xff0c;像int[ ]等。本文将讲述怎么用Java反射来创建数组和获取数组的Class对象。下面是所涵盖的主题列表&#xff1a;java.lang.reflect.ArrayCreating ArraysAccessing Ar…

定时执行java程序_如何让Java程序定时运行

由于项目开发的需要&#xff0c;必须实现让一个Java程序定时运行。比如&#xff0c;我的项目中&#xff0c;有一个网络蜘蛛&#xff0c;需要从互联网上抓取数据&#xff0c;与其配合&#xff0c;有另一个程序来对新抓取的页面进行索引的创建&#xff0c;由于数据源更新频率不高…