随着偏移量的增加,limit语句的执行会更加耗时,那么这是为什么呢?

随着偏移量的增加,limit语句的执行会更加耗时,那么这是为什么呢?
在业务中实现分页功能就离不了MySQL的limit语句,而随着数据表中数据量的增加,则不可避免会导致查询时偏移量过大。
我们知道随着偏移量的增大,limit语句的耗时会增加,接下来我们就探讨下如何去更好的处理limit的耗时问题。

一、实验
1、MySQL版本:

mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.18    |
+-----------+
1 row in set (0.00 sec)

2、实验表结构:

mysql> desc t213;
+-------+------------------+------+-----+---------+----------------+
| Field | Type             | Null | Key | Default | Extra          |
+-------+------------------+------+-----+---------+----------------+
| id    | int(10)          | NO   | PRI | NULL    | auto_increment |
| a     | int(10) unsigned | NO   | MUL | 0       |                |
| b     | int(10) unsigned | NO   |     | 0       |                |
+-------+------------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

其中,id为自增主键,字段a为普通索引

3、实验数据量近200万:

mysql> select count(*) from t213;
+----------+
| count(*) |
+----------+
|  1979311 |
+----------+
1 row in set (0.11 sec)

4、开始测试:
当偏移量为100万时:

mysql> select * from t213 where a=4 limit 1000000,10;
+---------+---+-----+
| id      | a | b   |
+---------+---+-----+
| 1000001 | 4 | 123 |
| 1000002 | 4 | 123 |
| 1000003 | 4 | 123 |
| 1000004 | 4 | 123 |
| 1000005 | 4 | 123 |
| 1000006 | 4 | 123 |
| 1000007 | 4 | 123 |
| 1000008 | 4 | 123 |
| 1000009 | 4 | 123 |
| 1000010 | 4 | 123 |
+---------+---+-----+
10 rows in set (2.00 sec)

我们知道以上的方法效率并不高,一般我们在数据量大的数据表中,不直接limit,而是通过连接去先查询id,再查询字段:

mysql> select c1.id, c1.a, c1.b from t213 c1 right join(select id from t213 where a=4 limit 1000000,10)c2 on c1.id=c2.id;
+---------+------+------+
| id      | a    | b    |
+---------+------+------+
| 1000001 |    4 |  123 |
| 1000002 |    4 |  123 |
| 1000003 |    4 |  123 |
| 1000004 |    4 |  123 |
| 1000005 |    4 |  123 |
| 1000006 |    4 |  123 |
| 1000007 |    4 |  123 |
| 1000008 |    4 |  123 |
| 1000009 |    4 |  123 |
| 1000010 |    4 |  123 |
+---------+------+------+
10 rows in set (0.16 sec)

这两种方法的效率相差巨大,那么为什么会如此呢?MySQL是如何执行相差巨大的两条语句的呢?

二、分析
根据高性能MySQL中关于limit的说明:
limit语句在偏移量巨大时,如select * from t213 where a=4 limit 1000000,10;。
对效率的影响主要在于MySQL会查询1,000,010条数据,并取最后10条,抛弃掉前面的1,000,000条。

也就是说,MySQL耗时耗力找到的数据,绝大部分都得废弃!
MySQL查找索引a的二级索引树,然后根据二级索引树上的主键值回表到聚簇索引树上进行扫描数据,为了limit而重复大量无用的IO操作

关于MySQL为什么limit时会遍历这么多数据,而不是遍历所需的几条,我们不去深究其设计原理,我们只分析下:

select c1.id, c1.a, c1.b from t213 c1 right join(select id from t213 where a=4 limit 1000000,10)c2 on c1.id=c2.id;

语句为何会比

select * from t213 where a=4 limit 1000000,10;

快那么多。

我们知道,MySQL中查询的数据会放在数据页中以便快速获取,
而系统表information_schema.innodb_buffer_page保存着InnoDB缓冲池中每个页面的信息。

我们在执行sql后查询innodb_buffer_page表中数据页的个数来判断下两个sql语句的不同之处。

t213表中有近200万数据
首先,重启MySQL服务,以便innodb_buffer_page表中t213测试表的数据页为空,然后执行不优化的sql:

mysql> select index_name,count(*) from information_schema.innodb_buffer_page-> where index_name in('a','primary') and table_name like '%t213%' group by index_name;
Empty set (0.07 sec)mysql> select * from test.t213 where a=4 limit 1000000,10;
+---------+---+-----+
| id      | a | b   |
+---------+---+-----+
| 1000001 | 4 | 123 |
| 1000002 | 4 | 123 |
| 1000003 | 4 | 123 |
| 1000004 | 4 | 123 |
| 1000005 | 4 | 123 |
| 1000006 | 4 | 123 |
| 1000007 | 4 | 123 |
| 1000008 | 4 | 123 |
| 1000009 | 4 | 123 |
| 1000010 | 4 | 123 |
+---------+---+-----+
10 rows in set (3.29 sec)mysql> select index_name,count(*) from information_schema.innodb_buffer_page -> where index_name in('a','primary') and table_name like '%t213%' group by index_name;
+------------+----------+
| index_name | count(*) |
+------------+----------+
| a          |      901 |
| PRIMARY    |     2156 |
+------------+----------+
2 rows in set (0.04 sec)

可以看到select * from test.t213 where a=4 limit 1000000,10;语句使用到901个二级索引a的索引数据页,使用到2156个聚簇索引数据页。

然后我们再次重启MySQL服务,确保innodb_buffer_page是空的,并执行优化的sql:

mysql> select index_name,count(*) from information_schema.innodb_buffer_page-> where index_name in('a','primary') and table_name like '%t213%' group by index_name;
Empty set (0.03 sec)mysql> select * from test.t213 c1 right join(select id from test.t213 where a=4 limit 1000000,10)c2 on c1.id=c2.id;
+---------+------+------+---------+
| id      | a    | b    | id      |
+---------+------+------+---------+
| 1000001 |    4 |  123 | 1000001 |
| 1000002 |    4 |  123 | 1000002 |
| 1000003 |    4 |  123 | 1000003 |
| 1000004 |    4 |  123 | 1000004 |
| 1000005 |    4 |  123 | 1000005 |
| 1000006 |    4 |  123 | 1000006 |
| 1000007 |    4 |  123 | 1000007 |
| 1000008 |    4 |  123 | 1000008 |
| 1000009 |    4 |  123 | 1000009 |
| 1000010 |    4 |  123 | 1000010 |
+---------+------+------+---------+
10 rows in set (0.22 sec)mysql> select index_name,count(*) from information_schema.innodb_buffer_page-> where index_name in('a','primary') and table_name like '%t213%' group by index_name;
+------------+----------+
| index_name | count(*) |
+------------+----------+
| a          |      901 |
| PRIMARY    |        3 |
+------------+----------+
2 rows in set (0.04 sec)

以上可以看到优化后的sql使用了聚簇索引树的3个数据页。

通过两个对比,我们可以发现,在select * from test.t213 c1 right join(select id from test.t213 where a=4 limit 1000000,10)c2 on c1.id=c2.id;
语句中,首先执行关联语句 select id from test.t213 where a=4 limit 1000000,10
使用到覆盖索引的概念,扫描二级索引树并获取到主键id值。
之后执行外部sql时,由于id已经找到,直接回表聚簇索引树查找响应id数据即可。

而执行未优化的select * from test.t213 where a=4 limit 1000000,10;语句时,
每一次在二级索引获取到的id值都需要回表,执行到最后才判断哪些数据是满足条件的,这样导致费力不讨好,效率很慢。

三、总结
高性能MySQL中提供有以下几种limit分页的优化方式:
1、join关联方式:select * from test.t213 c1 right join(select id from test.t213 where a=4 limit 1000000,10)c2 on c1.id=c2.id;
2、主键递增的表,每次分页记录上次的最大id值,下次分页查询通过判断id > last_id_num来执行:select * from test.t213 where id>1000000 and a=4 limit 10;
3、主键递增的表,通过between id值来执行分页:select * from test.t213 where a=4 and id between 1000001 and 1000010;
一般来说2,3两种方法虽然效率更高,但是局限性稍大。

实际项目中,针对分页我们要注意,随着数据量的增加,如果limit使用不当,分页效率会越来越慢,导致接口响应时间增加,用户友好度下降。
编写sql时使用合适的limit方式,会减少很多不必要的问题。

mysql ---- limit使用方式相关推荐

  1. Mysql limit用法

    MYSQL limit用法 1.Mysql的limit用法 在我们使用查询语句的时候,经常要返回前几条或者中间某几行数据,这个时候怎么办呢?不用担心,mysql已经为我们提供了这样一个功能. Sql代 ...

  2. mysql limit 运算符_MYSQL LIMIT 用法

    小太阳 MySQL limit 应用的一些例子. 语法格式: SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset 解析:LIMI ...

  3. Java连接mysql数据库的方式,java连接mysql数据库的方式(4句语句)

    1 加载mysql驱动: class.forName("con.mysql.jdbc.Driver").newInstance(); 2 根据数据库路径url,账号,密码进行数据库 ...

  4. Linux下连接Mysql服务器的方式

    一:mysql连接简介 1.linux下mysql的连接方式有三种: 本地mysql命令连接 客户端命令连接 脚本语言封装方法连接 2.linux下mysql的连接方法有两种: TCP/IP协议连接 ...

  5. python读取mysql以html形式输出_python实现处理mysql结果输出方式

    在运维过程中,经常需要读取mysql的结果输出并进行响应的处理,这节介绍如何用Python读取mysql结果表并进行相应的整理. 进行mysql结果文件输出: mysql -h10.20.10.207 ...

  6. mysql limit 分页 0_Mysql分页之limit用法与limit优化

    Mysql limit分页语句用法 与Oracle和MS SqlServer相比,mysql的分页方法简单的让人想哭. --语法: SELECT * FROM table LIMIT [offset, ...

  7. Mysql limit 子查询

    为什么80%的码农都做不了架构师?>>> (1)mysql limit 不支持子查询像下面这条语句无法执行      SELECT  * FROM b_c_s1 where CT_I ...

  8. mysql二进制方式_MySQL数据库之MySql二进制连接方式详解

    本文主要向大家介绍了MySQL数据库之MySql二进制连接方式详解 ,通过具体的内容向大家展现,希望对大家学习MySQL数据库有所帮助. 使用mysql二进制方式连接 您可以使用MySQL二进制方式进 ...

  9. 怎样把MySQL的编码方式改为utf8?

    一.当我们安装好MySQL后,单击电脑开始,然后运行cmd,记得必须要用管理员的身份运行.然后输入net start mysql 前面操作如果忘记采用管理员身份运行的话,会出现系统访问错误. 二.修改 ...

最新文章

  1. 除了 AI,这些技术为 IIoT 插上飞向“4.0”的翅膀
  2. 多维数组的索引与切片_「GCTT 出品」Go 系列教程——11. 数组和切片
  3. 利用 Arthas 精准定位 Java 应用 CPU 负载过高问题
  4. 音视频技术开发周刊 54期
  5. Handbook of Constraints Programming——Chapter 22 Constraint-Based Scheduling and Planning
  6. 微软 azure_在Microsoft Azure上运行Eclipse MicroProfile
  7. 保存的图数据丢失_锡柴自主刷写和备份共享数据文件使用介绍
  8. 密码学的数学模型及其理论基础 【二】
  9. 无法解析的外部符号+_mysql_fetch_row_vs连接mysql出现以下错误,求解答,谢谢,不胜感激...
  10. java 分页查询实例_JavaWeb学习之分页查询实例
  11. linux 文件夹大小_技能“慧”|初识Linux(二)
  12. 2017.3.11[bzoj2440][中山市选2011]完全平方数
  13. OSChina 周日乱弹——幸福来的太突然
  14. Unity 基于图像处理的图像显示特效制作过场特效
  15. 怎么恢复删除的文件?实用小妙招
  16. 怎样将HTML保存到d盘,怎么保存网页 保存整个网页的办法
  17. Kali Linux忘记密码怎么办?
  18. 字符串函数的使用和剖析(三)
  19. java编程语言基础外文,Java编程语言基础(外文文献翻译)
  20. CMake生成多个.so文件

热门文章

  1. 微型计算机中abcd是指,一级笔试模拟试题二(答案)
  2. java telnet乱码_telnet乱码的解决方法
  3. go mysql slave_【Golang+mysql】记一次mysql数据库迁移(一)
  4. java mvc ef_一个简单MVC5 + EF6示例分享
  5. java装饰模式模拟流_Java 装饰模式 io流
  6. java jsch 密钥登陆_我们可以使用JSch进行基于SSH密钥的通信吗?
  7. ECMAScript标准资料
  8. linux:系统对open files的限制
  9. 【maven】使用(阿里云 aliyun)镜像仓库
  10. linux查端口占用