往期热门文章:

1、《往期精选优秀博文都在这里了!》2、防止缓存击穿?请用布隆过滤器!3、Java 14 开箱指南!你强任你强!4、start.aliyun.com 正式上线!极速构建 Spring Cloud 应用5、腾讯和阿里在B站“打起来了”,你何时见过这场面?

本文来源:zhangyachen.github.io

一,前言

首先说明一下MySQL的版本:

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

表结构:

mysql> desc test;+--------+---------------------+------+-----+---------+----------------+| Field  | Type                | Null | Key | Default | Extra          |+--------+---------------------+------+-----+---------+----------------+| id     | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment || val    | int(10) unsigned    | NO   | MUL | 0       |                || source | int(10) unsigned    | NO   |     | 0       |                |+--------+---------------------+------+-----+---------+----------------+3 rows in set (0.00 sec)

id为自增主键,val为非唯一索引。

灌入大量数据,共500万:

mysql> select count(*) from test;+----------+| count(*) |+----------+|  5242882 |+----------+1 row in set (4.25 sec)

我们知道,当limit offset rows中的offset很大时,会出现效率问题:

mysql> select * from test where val=4 limit 300000,5;+---------+-----+--------+| id      | val | source |+---------+-----+--------+| 3327622 |   4 |      4 || 3327632 |   4 |      4 || 3327642 |   4 |      4 || 3327652 |   4 |      4 || 3327662 |   4 |      4 |+---------+-----+--------+5 rows in set (15.98 sec)

为了达到相同的目的,我们一般会改写成如下语句:

mysql> select * from test a inner join (select id from test where val=4 limit 300000,5) b on a.id=b.id;+---------+-----+--------+---------+| id      | val | source | id      |+---------+-----+--------+---------+| 3327622 |   4 |      4 | 3327622 || 3327632 |   4 |      4 | 3327632 || 3327642 |   4 |      4 | 3327642 || 3327652 |   4 |      4 | 3327652 || 3327662 |   4 |      4 | 3327662 |+---------+-----+--------+---------+5 rows in set (0.38 sec)

时间相差很明显。

为什么会出现上面的结果?我们看一下select * from test where val=4 limit 300000,5;的查询过程:

  • 查询到索引叶子节点数据。
  • 根据叶子节点上的主键值去聚簇索引上查询需要的全部字段值。

类似于下面这张图:

image

像上面这样,需要查询300005次索引节点,查询300005次聚簇索引的数据,最后再将结果过滤掉前300000条,取出最后5条。MySQL耗费了大量随机I/O在查询聚簇索引的数据上,而有300000次随机I/O查询到的数据是不会出现在结果集当中的。

肯定会有人问:既然一开始是利用索引的,为什么不先沿着索引叶子节点查询到最后需要的5个节点,然后再去聚簇索引中查询实际数据。这样只需要5次随机I/O,类似于下面图片的过程:

image

其实我也想问这个问题。

证实

下面我们实际操作一下来证实上述的推论:

为了证实select * from test where val=4 limit 300000,5是扫描300005个索引节点和300005个聚簇索引上的数据节点,我们需要知道MySQL有没有办法统计在一个sql中通过索引节点查询数据节点的次数。我先试了Handler_read_*系列,很遗憾没有一个变量能满足条件。

我只能通过间接的方式来证实:

InnoDB中有buffer pool。里面存有最近访问过的数据页,包括数据页和索引页。所以我们需要运行两个sql,来比较buffer pool中的数据页的数量。预测结果是运行select * from test a inner join (select id from test where val=4 limit 300000,5) b>之后,buffer pool中的数据页的数量远远少于select * from test where val=4 limit 300000,5;对应的数量,因为前一个sql只访问5次数据页,而后一个sql访问300005次数据页。

select * from test where val=4 limit 300000,5mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name;Empty set (0.04 sec)

可以看出,目前buffer pool中没有关于test表的数据页。

mysql> select * from test where val=4 limit 300000,5;+---------+-----+--------+| id      | val | source |+---------+-----+--------+| 3327622 |   4 |      4 || 3327632 |   4 |      4 || 3327642 |   4 |      4 || 3327652 |   4 |      4 || 3327662 |   4 |      4 |+---------+-----+--------+5 rows in set (26.19 sec)mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name;+------------+----------+| index_name | count(*) |+------------+----------+| PRIMARY    |     4098 || val        |      208 |+------------+----------+2 rows in set (0.04 sec)

可以看出,此时buffer pool中关于test表有4098个数据页,208个索引页。

`select * from test a inner join (select id from test where val=4 limit 300000,5)`` b>为了防止上次试验的影响,我们需要清空buffer pool,重启mysql。

mysqladmin shutdown/usr/local/bin/mysqld_safe &mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name;Empty set (0.03 sec)

运行sql:

mysql> select * from test a inner join (select id from test where val=4 limit 300000,5) b on a.id=b.id;+---------+-----+--------+---------+| id      | val | source | id      |+---------+-----+--------+---------+| 3327622 |   4 |      4 | 3327622 || 3327632 |   4 |      4 | 3327632 || 3327642 |   4 |      4 | 3327642 || 3327652 |   4 |      4 | 3327652 || 3327662 |   4 |      4 | 3327662 |+---------+-----+--------+---------+5 rows in set (0.09 sec)mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name;+------------+----------+| index_name | count(*) |+------------+----------+| PRIMARY    |        5 || val        |      390 |+------------+----------+2 rows in set (0.03 sec)

我们可以看明显的看出两者的差别:第一个sql加载了4098个数据页到buffer pool,而第二个sql只加载了5个数据页到buffer pool。符合我们的预测。也证实了为什么第一个sql会慢:读取大量的无用数据行(300000),最后却抛弃掉。

而且这会造成一个问题:加载了很多热点不是很高的数据页到buffer pool,会造成buffer pool的污染,占用buffer pool的空间。

遇到的问题

为了在每次重启时确保清空buffer pool,我们需要关闭innodb_buffer_pool_dump_at_shutdown和innodb_buffer_pool_load_at_startup,这两个选项能够控制数据库关闭时dump出buffer pool中的数据和在数据库开启时载入在磁盘上备份buffer pool的数据。

往期热门文章:

1、《历史文章分类导读列表!精选优秀博文都在这里了!》2、防止缓存击穿?请用布隆过滤器!3、Java 14 开箱指南!你强任你强!4、start.aliyun.com 正式上线!极速构建 Spring Cloud 应用5、腾讯和阿里在B站“打起来了”,你何时见过这场面?6、面试必备41道 SpringBoot 面试题,不看亏大了!7、线上服务 CPU 100%?一键定位 so easy!8、差距大,没想到成人网站用的技术都甩我们几条街!9、6个接私活的网站,你有技术就有钱!10、网上找的一段代码突然爆了,项目出现大Bug!

sql limit不接具体数字_MySQL的Limit 性能差?真的不能再用了?相关推荐

  1. sql limit不接具体数字_MySQL面试题-SQL优化

    1. SQL的生命周期? 应用服务器与数据库服务器建立一个连接 数据库进程拿到请求sql 解析并生成执行计划,执行 读取数据到内存并进行逻辑处理 通过步骤一的连接,发送结果到客户端 关掉连接,释放资源 ...

  2. sql limit不接具体数字_SQL别再秀操作了,这么写吧还是。

    sql执行顺序: 1.LIMIT 语句: 分页查询,是最常用的场景之一,但也通常也是最容易出问题.比如对于简单的语句,一般 DBA ,想到的办法是在 type, name, create_time 字 ...

  3. sql limit不接具体数字_这21个写SQL的好习惯,你要养成呀

    前言 每一个好习惯都是一笔财富,本文分SQL后悔药, SQL性能优化,SQL规范优雅三个方向,分享写SQL的21个好习惯,谢谢阅读,加油哈~ 公众号:「捡田螺的小男孩」 1. 写完SQL先explai ...

  4. sql中limit后参数有引号_mySql limit 函数后的参数是什么意思

    展开全部 mySql limit 函数语法为SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset.LIMIT 接受一个或两个数字参 ...

  5. mysql limit 动态参数_MySQL中limit使用动态参数的解决方法(拼接SQL字符串语句来执行SQL)...

    官方好像说过limit已经在5.6版本上支持了动态参数,但是测试时依然还是不行. 那么要解决limit动态参数唯一能做的就是使用字符串SQL拼接的形式,然后再进行执行. 一般有以下方式解决: 1.存储 ...

  6. mysql 优化300例_mysql的limit优化实例

    测试环境 操作系统: debian linux 服务器版本: Mysql 5.0.24 Mysql数据库的Qcache缓存关闭 数据库表testtable的参数: 类型:     MyISAM 大小: ...

  7. SQL Server 2008 R2——使用数字辅助表(master..spt_values)实现用计数字段对记录进行重复显示...

    SQL Server 2008 R2--使用数字辅助表(master..spt_values)实现用计数字段对记录进行重复显示 原文:SQL Server 2008 R2--使用数字辅助表(maste ...

  8. sql查询非11位非数字_非生产环境SQL查询性能调优技巧

    sql查询非11位非数字 It is a common misconception that you need real production data, or production like dat ...

  9. 分页offset格式_MySQL中limit分页查询性能问题分析

    MySQL Limit 语法格式: SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset 分页查询时,我们会在 LIMIT 后面传 ...

  10. oracle如何判断数字中有字母,SQL 判断含有字母和数字的字符串

    SQL 判断含有字母和数字的字符串,你想从一个表里筛选出部分行数据,筛选条件是你感兴趣的那个列只包含字母和数字字符,考虑下面的视图 V(SQL Server 用户需要把字符串连接操作符||替换为 +) ...

最新文章

  1. SpringBoot第十七篇:上传文件
  2. 脑机接口猴子通过“意念”打游戏!马斯克:未来能让瘫痪者用意念玩手机
  3. springboot + elasticsearch
  4. ImportError: No module named pil
  5. VTK:Rendering之AmbientSpheres
  6. windows phone 操作 http异步返回结果
  7. android颜色选择状态,androidUiAutomator如何根据颜色判断控件的状态
  8. 单调队列优化和决策单调性优化
  9. cocoaPod集成9大环境以及报错项目问题
  10. [转]常见的用户密码加密方式以及破解方法
  11. 灵活强大的构建系统Gradle
  12. 23_python基础—模块和包
  13. Python花式编程案例锦集(2)
  14. python编程特点_Python基础(1)--Python编程习惯与特点
  15. 华为服务器SNMP协议怎么修改,华为迈普交换机、瑞斯康达SNMP协议配置方法
  16. python 比对两个excel表数据_Python第17课:两个数据表如何比对碰撞?(二)
  17. 由WMT机器翻译双向夺冠看搜狗语音交互布局
  18. 计算机改硬盘格式,硬盘格式转换,详细教您如何将硬盘mbr格式转换为gpt格式
  19. 微信小程序网络请求api中HTML格式问题
  20. Typroa导出HTML带大纲

热门文章

  1. vi 打开文件,行末尾有^M
  2. Git : SSH 协议服务器
  3. 教你手工去迅雷广告『罗斌原创』
  4. 无法解析的外部符号 __imp_RegCloseKey
  5. 函数 php_PHP丨PHP基础知识之PHP基础入门——函数「理论篇」
  6. word保存不了磁盘已满_【Word技巧】word使用终极技巧,工程人必会(二)
  7. 使用html的a标签,无法转跳到servlet页面中的解决办法,适用于servlet的初学者,不与框架相兼容
  8. android 文件上传参数,Android OkHttp Post上传文件并且携带参数实例详解
  9. android studio react native 模拟器,Windows下搭建React Native环境与Android Studio集成
  10. mysql查询一个月未登录_mysql函数实例-统计1周内未登陆的用户