背景

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

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

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

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

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

这是为什么呢?

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

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

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

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

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

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

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

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

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

优化方式

1、使用覆盖索引

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

总结

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

参考资料

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

MySQL优化之超大分页查询相关推荐

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

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

  2. Mysql给普通和分页查询结果加序号

    Mysql给普通和分页查询结果加序号 一.效果展示 1.普通查询加序号 2.分页查询加序号 二.表结构以及数据 三.解释说明 1.解释 2.相关知识点 3.= 和 := 的区别: 一.效果展示 1.普 ...

  3. MySQL百万级数据分页查询及优化

    点击蓝色"程序猿DD"关注我哟 加个"星标",不忘签到哦 来源:https://www.cnblogs.com/geningchao 关注我,回复口令获取可获取 ...

  4. MySQL大数据量分页查询方法及其优化

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:收藏了!7 个开源的 Spring Boot 前后端分离优质项目个人原创+1博客:点击前往,查看更多 链接:ht ...

  5. MySQL分优化之超大页查询

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

  6. 千锋重庆Java学习之MySQL大数据量分页查询方法及其优化

    方法1: 直接使用数据库提供的SQL语句 语句样式: MySQL中,可用如下方法: SELECT * FROM 表名称 LIMIT M,N 适应场景: 适用于数据量较少的情况(元组百/千级) 原因/缺 ...

  7. MySQL优化篇:慢查询日志

    1.概念 MySQL的慢查询日志是MySQL提供的一种日志记录,他用来记录在MySQL中响应时间超过阈值的语句,具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中. ...

  8. jsp mysql模板_jsp的分页查询的代码(mysql数据库)

    推荐:JSP的MYSQL数据库的代码最近研究了jsp的数据库操作问题.本来我是要拿oracle数据库来测试的.但是由于机子的内存限制,就换成了mysql数据库了. 第一次用jsp编数据库的连接,发现问 ...

  9. Oracle、MySql、SQLServer 数据库分页查询语句

    (一).** mysql的分页查询** mysql的分页查询是最简单的,借助关键字limit即可实现查询,查询语句通式: /* * sql:可以是单表的查询语句,也可以是多表的联合查询语句* firs ...

最新文章

  1. PHPRAP v1.0.8 版本发布,安装时数据库不存在自动创建
  2. Webpack使用指南
  3. LeetCode 1521. 找到最接近目标值的函数值(位运算)
  4. ExoPlayer用户手册
  5. 操作系统之文件管理:9、磁盘的结构与磁盘调度算法(先来先服务FCFS、最短寻找时间优先SSTF、扫描算法SCAN、循环扫描算法C-SCAN、LOOK调度算法、C-LOOK调度算法)
  6. 540 - Team Queue
  7. Git笔记(24) 维护项目
  8. OCR文字识别技术总结(二)
  9. 使用JMH做Java微基准测试(一)测试入门
  10. mysql 删除用户下的所有表_使用PL/SQL快速删除用户下的所有表数据
  11. 【华为 OJ 】成绩排序
  12. Adobe Spark试用手记
  13. LATEX 罗马数字的输入
  14. 10060 mysql_MySQL错误:Can't connect to MySQL server (10060) 解决方案
  15. 激光测距仪传感器,基于EFM32控制器开发设计_方案
  16. python tk窗口 选择 销毁_Python tkinter - 删除其他窗口 - 已调用tk.withdraw()
  17. 云上城之歌和你好呀勇士同一个服务器吗,云上城之歌你好呀勇士官网版
  18. TOJ 4074 Running Laps -- 树状数组
  19. mac下的socket调试工具---sokit
  20. [zf from byhh]百度完整的面试经历

热门文章

  1. mysql中information_schema.columns字段说明
  2. python之Numpy随机抽样
  3. numpy下 随机抽样
  4. H264视频压缩算法
  5. HotSpot VM垃圾收集器——Serial Parallel CMS G1垃圾收集器的JVM参数、使用说明、GC分析
  6. ld链接时提示接口未实现_苹果耳机一分二转接口,可以边听歌边充电!
  7. 指定搜索词的分词方法——elasticsearch
  8. GBT 25000.51-2010 软件工程 软件产品质量要求与评价(SquaRE)商业现货(COTS)软件产品的质量要求和测试细则...
  9. 生产者消费问题以及多生产者—消费者问题实现思想
  10. 使用JS获取input值