≡
  • 网络编程
  • 数据库
  • CMS技巧
  • 软件编程
  • PHP笔记
  • JavaScript
  • MySQL
位置:首页 > 数据库 > MySQL

MySQL超大分页查询的简单示例

人气:332 时间:2021-08-18

这篇文章主要为大家详细介绍了MySQL超大分页查询的简单示例,具有一定的参考价值,可以用来参考一下。

感兴趣的小伙伴,下面一起跟随四海网的小玲来看看吧!

目录
  • 背景
  • LIMIT优化
  • 优化方式
    • 1、使用覆盖索引
    • 2、子查询优化
    •  3、延迟关联
    • 4、记录上次查询结束的位置
  • 总结

 

背景

基本上只要是做后台开发,都会接触到分页这个需求或者功能吧。基本上大家都是会用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();

MySQL优化教程之超大分页查询

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

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

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

代码如下:


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

MySQL优化教程之超大分页查询

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

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

代码如下:


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

MySQL优化教程之超大分页查询

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

这是为什么呢?

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

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

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

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

代码如下:



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

MySQL优化教程之超大分页查询

代码如下:



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

MySQL优化教程之超大分页查询

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

 

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

 

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

 

优化方式

 

1、使用覆盖索引

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

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

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

代码如下:



# 没有使用覆盖索引
select * from t5 order by text limit 1000000, 10;

MySQL优化教程之超大分页查询

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

代码如下:



# 使用了覆盖索引
select id, `text` from t5 order by text limit 1000000, 10;

MySQL优化教程之超大分页查询

从上面的对比中,超大分页查询中,使用了覆盖索引之后,花了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;

MySQL优化教程之超大分页查询

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

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

  • 这种写法,要求主键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;

MySQL优化教程之超大分页查询

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

 

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

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

代码如下:



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

MySQL优化教程之超大分页查询

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

 

总结

  • 介绍了超大分页查询性能过差的原因,还有分享了几个优化思路
  • 超大分页的优化思路就是让分页的SQL尽量在最佳的性能区间执行,不要触发全表扫描即可
  • 希望以上的分享,可以让你们在MySQL这条路上少走弯路~~~

 

参考资料

 

  • 《MySQL性能优化》第六章 查询优化性能
  • 《数据库查询优化器的艺术》

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

本文来自:http://www.q1010.com/177/19625-0.html

注:关于MySQL超大分页查询的简单示例的内容就先介绍到这里,更多相关文章的可以留意四海网的其他信息。

关键词:分页查询

您可能感兴趣的文章

  • MySQL百万级数据量分页查询方法及其优化的简单示例
  • MySQL 分页查询优化的简单示例
  • MySQL分页查询的两个比较示例
  • php下巧用select语句实现MySQL分页查询
  • MySQL分页原理和高效率的MySQL分页查询语句
  • SQL Server SQL分页查询示例
  • SQL Server SQL数据分页查询的简单示例
  • SQL Server分页查询处理的简单示例
  • SQL Server sql分页查询几种写法示例
  • SQL Server 和Oracle分页查询示例
上一篇:MySQL admin获取一个MySQL实例当前的TPS和QPS的简单示例
下一篇:MySQL5.5 部署的一个问题的深入分析
热门文章
  • mysql 修改character_set_server为utf-8的简单示例
  • 解决MySQL丢失文件localhost.localdomain.pid、mysql.sock的示例
  • MySQL 数据类型binary和varbinary的简单示例
  • MySQL:reading initial communication packet问题解决方法
  • MySql 表类型MYISAM、InnoDB区别
  • bash: mysql: command not found 的解决方法
  • MYSQL默认用户名ROOT修改方法
  • MySQL 常用命令菜鸟教程
  • MySQL 使用命令行新建用户并授予权限
  • MySql 数据库物理文件存放位置查看示例
  • 最新文章
    • MySQL查看死锁与解除死锁的简单示例
    • MySQL 慢查询的功能实例
    • MySQL查看死锁与去除死锁的简单示例
    • MySQL找出未提交事务的SQL的简单示例
    • MySQL锁阻塞的的简单示例
    • MySQL中的binary类型使用操作的示例
    • SQL优化教程之in与range查询的简单示例
    • MySQL 的 21 个规范、优化最佳实践!
    • MySQL 字符类型大小写敏感的简单示例
    • 解决mybatis-plus分页传入参数后sql where条件没有limit分页信息的问题

四海网收集整理一些常用的php代码,JS代码,数据库mysql等技术文章。