limit mysql 性能_MySQL 用 limit 为什么会影响性能?
一.前言
首先说明一下MySQL的版本:
mysql> selectversion();+-----------+
| version() |
+-----------+
| 5.7.17 |
+-----------+
1 row in set (0.00 sec)
表结构:
mysql> desctest;+--------+---------------------+------+-----+---------+----------------+
| 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.00sec)
id为自增主键,val为非唯一索引。
灌入大量数据,共500万:
mysql> select count(*) fromtest;+----------+
| 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;的查询过程:
查询到索引叶子节点数据。
根据叶子节点上的主键值去聚簇索引上查询需要的全部字段值。
类似于下面这张图
像上面这样,需要查询300005次索引节点,查询300005次聚簇索引的数据,最后再将结果过滤掉前300000条,取出最后5条。MySQL耗费了大量随机I/O在查询聚簇索引的数据上,而有300000次随机I/O查询到的数据是不会出现在结果集当中的。
肯定会有人问:既然一开始是利用索引的,为什么不先沿着索引叶子节点查询到最后需要的5个节点,然后再去聚簇索引中查询实际数据。这样只需要5次随机I/O,类似于下面图片的过程:
其实我也想问这个问题。
证实
下面我们实际操作一下来证实上述的推论:
为了证实
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 byindex_name;
Emptyset (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 byindex_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 byindex_name;
Emptyset (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 byindex_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.https://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/
2.https://dev.mysql.com/doc/refman/5.7/en/innodb-information-schema-buffer-pool-tables.html
作者:zhangyachen
来源:https://dwz.cn/K1Q1cePW
limit mysql 性能_MySQL 用 limit 为什么会影响性能?相关推荐
- mysql查询时,offset过大影响性能的原因与优化方法
遇到的问题 我们大家都知道,mysql查询使用select命令,配合limit,offset参数可以读取指定范围的记录,但是offset过大影响查询性能的原因及优化方法,这次工作中因为要导出40W的数 ...
- mysql limit 分页 优化_MYSQL分页limit速度太慢优化方法
在mysql中limit可以实现快速分页,但是如果数据到了几百万时我们的limit必须优化才能有效的合理的实现分页了,否则可能卡死你的服务器哦. 当一个表数据有几百万的数据的时候成了问题! 如 * f ...
- MySQL中where 1=1真的会影响性能么?
先说结论,where 1=1不会影响性能,如有错误,请指点一下 trim标记是一个格式化的标记,可以完成set或者是where标记的功能. 2020.1.17,周五,距离春节假期返家仅剩两天,在公司抽 ...
- mysql limit 动态参数_MySQL中limit使用动态参数的解决方法(拼接SQL字符串语句来执行SQL)...
官方好像说过limit已经在5.6版本上支持了动态参数,但是测试时依然还是不行. 那么要解决limit动态参数唯一能做的就是使用字符串SQL拼接的形式,然后再进行执行. 一般有以下方式解决: 1.存储 ...
- distinct mysql性能_MySQL中distinct和group by性能比较
MySQL中distinct和group by性能比较[转] 之前看了网上的一些测试,感觉不是很准确,今天亲自测试了一番.得出了结论(仅在个人计算机上测试,可能不全面,仅供参考) 测试过程: 准备一张 ...
- mysql外键写了会怎么样_mysql使用外键会影响性能吗
推荐答案 孔雀丫丫 2015.04.10 采纳率:87% 等级:38 已帮助:18万人 外键约束对子表的含义: 如果在父表中找不到候选键,则不允许在子表上进行insert/update 外键约束 ...
- 影响索引的mysql函数_mysql索引对排序的影响实例分析
本文实例讲述了mysql索引对排序的影响.分享给大家供大家参考,具体如下: 索引不仅能提高查询速度,还可以添加排序速度,如果order by 后面的语句用到了索引,那么将会提高排序的速度. 测试 1. ...
- limit mysql 取最后_世上最全mysql性能调优总结
对于 select*要时刻保持谨慎的态度 绝大多数情况,是不需要 select*的.一旦使用了这种语句,便会让优化器无法完成索引覆盖扫描这类优化,而且还会增加额外的I/O.内存和CPU的消耗. 当然, ...
- mysql limit 算法_MySQL的Limit详解
问题:数据库查询语句,如何只返回一部分数据? Top子句 TOP 子句用于规定要返回的记录的数目.对于拥有数千条记录的大型表来说,TOP 子句是非常有用的. 在SQL Server数据库中语法为: S ...
最新文章
- 腾讯云发布全链路数据开发平台WeData,大数据开发迈入新时代
- Python基础教程:迭代和解析
- win8下面卸载MYSQL数据库
- python3精要(23)-递归与函数列表
- [java][工具类][Arrays]
- 编译调试 .NET Core 5.0 Preview 并分析 Span 的实现原理
- C# 反射机制(转)
- sql跨表查询_白话django之ORM的查询语句
- 计算机中丢失dinput8,修复dnfdinput8.dll
- Stale branches 设置_Mac OS 网络设置教程 wifi设置与宽带设置详解
- php实现从尾到头打印列表
- 20200221_2_国家非文化遗产聚类分析
- java 获取class的方法_[Java教程]Java反射定义、获取Class三种方法
- Eclipse中快速查找类或代码
- qcc512x_qcc302x开发调试笔记
- 测试开发大厂面试精选40题
- 主数据及其管理MDM
- 獨立屋裝修/村屋裝修
- libsvmpredict和svmtrain的参数和返回值
- 通过js实现金字塔(星号金字塔,回文对称数字金字塔)
热门文章
- [easyui] 在iframe中操作父窗口的组件
- 【CSS3】table的css属性
- Memcache的使用
- 2012-02-14 貌似情人节
- Javascript中call的使用
- mysql怎么使用sql语句查看表的编码_MySQL中使用SQL语句查看某个表的编码
- 遍历矩阵每一行穷举_LeetCode:二维数组—旋转矩阵
- jtree+mysql_Swing: JTree显示 图标 + 文字 | 学步园
- 离开当前屏幕的判断方法_Android App内存泄露测试方法总结
- HTML中From表单的常用type属性及用法【较全】