MySQL 用 limit 为什么会影响性能?

原文链接:
https://mp.weixin.qq.com/s/XvtKuCeb8UWFAFsQM6kVhg

一,前言

首先说明一下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;的查询过程:

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

类似于下面这张图:

像上面这样,需要查询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,5

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.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(*)frominformation_schema.INNODB_BUFFER_PAGEwhere INDEX_NAMEin('val','primary')and TABLE_NAMElike'%test%'group by index_name;Emptyset (0.03 sec)运行sql:mysql>select*from test ainner join (select idfrom testwhere val=4limit300000,5) bona.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 rowsinset (0.09 sec)mysql>select index_name,count(*)frominformation_schema.INNODB_BUFFER_PAGEwhere INDEX_NAMEin('val','primary')and TABLE_NAMElike'%test%'group by index_name; ------------ ---------- | index_name |count(*) | ------------ ---------- | PRIMARY |5 || val |390 | ------------ ---------- 2 rowsinset (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

(0)

相关推荐

  • 头条二面: 详解一条 SQL 的执行过程

    前言 天天和数据库打交道,一天能写上几十条 SQL 语句,但你知道我们的系统是如何和数据库交互的吗?MySQL 如何帮我们存储数据.又是如何帮我们管理事务?....是不是感觉真的除了写几个 「sele ...

  • 数据库 | 001-MySQL梳理系列(一)

    MySQL基本组成 SQL执行流程 Server 层主要包括连接器.查询缓存.分析器.优化器.执行器,包含了MySQL主要的很多核心功能,以及所有的内置函数.存储过程.触发器.视图等,其实就是所有跨存 ...

  • case when影响性能吗

    近年来线缆行业的发展和创新几乎都和材料有关,音视频线缆行业中最常用的是三种材料,PVC聚氯乙烯/PE聚乙烯/XLPE交联聚乙烯,那么对于外行如何区别这三者呢?对音视频信号有多大影响呢?如何选择呢? 外 ...

  • mysql innode和myisam引擎查询性能比较测试

    百度了一遍下来都在说myisam引擎的查询性能比innodb好,但是没有看到拿数据出来说话的,今天得空就做了下测试. 知识回顾 摘抄自:https://blog.csdn.net/STFPHP/art ...

  • 手机到底应不应该插SD卡?会影响性能?

    在购买手机的时候,手机容量是一个无法避免的问题,到底多少容量合适,买多少容量的手机划算,拓展SD卡容量是否靠谱等等,都成为了大家关注的话题.特别是对于Android机型来说,手机内置存储容量的折算价格 ...

  • 手机到底应不应该插SD卡? 会影响性能?

    手机储存的重要性对于很多人来说是非常的重要,储存空间的大小有时候甚至能够影响一款手机的使用寿命,当我们经常性的遇到手机储存不足时,我们就会想要买更大储存的手机.而对于安卓手机而言,目前依旧还有另外一种 ...

  • 【MySQL】LIMIT以及LIMIT OFFSET

    LIMIT两种方法: 1 两种方法: 2 (1)LIMIT A; 3 #表示从第一条记录开始取A条记录: 4 5 (2)LIMIT A,B; 6 #参数A为可选参数,表示跳过A条数据(默认为0) 7 ...

  • 陶瓷坯料配方对变形的影响性能

    坯用三大原料的选择 一 粘土的选择 在选择粘土原料时,干燥收缩大.敏感指数高的粘土尽可能少用或不用.须选用粘性大.收缩率中等.吸水率较小等特性的粘土. 对于粘土类原料,要注意两点: 一是含砂量不宜太高 ...

  • 装机不求人:主板板型会影响性能么?

    主板的尺寸各不相同,常见主板板型有三种:标准ATX.M-ATX以及ITX.那么这些主板板型会影响性能么?今天我们就来一起探究下. 标准ATX: 标准ATX是最常见的主板,也是我们常说的大板,尺寸方面, ...

  • 电子密封胶可以进行脱泡处理吗?发黄之后会影响性能吗?

    使用电子密封胶时,一旦出现气泡,将会影响后期的固化效果.有些用户为了避免一系列问题的出现,提前进行脱泡处理.不过并不是所有粘接物件都可以进行脱泡,需要多加注意. 电子密封胶可以进行脱泡处理吗? 如果灌 ...

  • Google想让你更健康用手机的Digital Wellbeing,被发现会影响性能

    近年智能手机的全面普及让大多数人都变成了"低头族",恨不得手机就和自己的手融为一体,这在全球范围都是个普遍的现象,为此现在手机硬软件厂商反而不希望大家日常玩这么多手机,比如苹果在i ...