• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • MsSql
  • Mysql
  • oracle
  • MariaDB
  • DB2
  • SQLite
  • PostgreSQL
  • MongoDB
  • Redis
  • Access
  • 数据库其它
  • sybase
  • HBase
您的位置:首页 > 数据库 >Mysql > 详解在mysql查询时,offset过大影响性能的原因与优化方法

详解在mysql查询时,offset过大影响性能的原因与优化方法

作者:匿名 字体:[增加 减小] 来源:互联网 时间:2018-12-05

匿名通过本文主要向大家介绍了mysql,offset等相关知识,希望本文的分享对您有所帮助

mysql查询使用select命令,配合limit,offset参数可以读取指定范围的记录。本文将介绍mysql查询时,offset过大影响性能的原因及优化方法。

准备测试数据表及数据

1.创建表

CREATE TABLE `member` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(10) NOT NULL COMMENT '姓名', `gender` tinyint(3) unsigned NOT NULL COMMENT '性别', PRIMARY KEY (`id`), KEY `gender` (`gender`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2.插入1000000条记录

<?php
$pdo = new PDO("mysql:host=localhost;dbname=user","root",'');for($i=0; $i<1000000; $i++){    $name = substr(md5(time().mt_rand(000,999)),0,10);    $gender = mt_rand(1,2);    $sqlstr = "insert into member(name,gender) values('".$name."','".$gender."')";    $stmt = $pdo->prepare($sqlstr);    $stmt->execute();}
?>mysql> select count(*) from member;
+----------+| count(*) |
+----------+|  1000000 |
+----------+1 row in set (0.23 sec)


3.当前数据库版本

mysql> select version();
+-----------+| version() |
+-----------+| 5.6.24    |
+-----------+1 row in set (0.01 sec)


分析offset过大影响性能的原因

1.offset较小的情况

mysql> select * from member where gender=1 limit 10,1;
+----+------------+--------+| id | name       | gender |
+----+------------+--------+| 26 | 509e279687 |      1 |
+----+------------+--------+1 row in set (0.00 sec)mysql> select * from member where gender=1 limit 100,1;
+-----+------------+--------+| id  | name       | gender |
+-----+------------+--------+| 211 | 07c4cbca3a |      1 |
+-----+------------+--------+1 row in set (0.00 sec)mysql> select * from member where gender=1 limit 1000,1;
+------+------------+--------+| id   | name       | gender |
+------+------------+--------+| 1975 | e95b8b6ca1 |      1 |
+------+------------+--------+1 row in set (0.00 sec)

当offset较小时,查询速度很快,效率较高。

2.offset较大的情况

mysql> select * from member where gender=1 limit 100000,1;
+--------+------------+--------+| id     | name       | gender |
+--------+------------+--------+| 199798 | 540db8c5bc |      1 |
+--------+------------+--------+1 row in set (0.12 sec)mysql> select * from member where gender=1 limit 200000,1;
+--------+------------+--------+| id     | name       | gender |
+--------+------------+--------+| 399649 | 0b21fec4c6 |      1 |
+--------+------------+--------+1 row in set (0.23 sec)mysql> select * from member where gender=1 limit 300000,1;
+--------+------------+--------+| id     | name       | gender |
+--------+------------+--------+| 599465 | f48375bdb8 |      1 |
+--------+------------+--------+1 row in set (0.31 sec)

当offset很大时,会出现效率问题,随着offset的增大,执行效率下降。

分析影响性能原因

select * from member where gender=1 limit 300000,1;

因为数据表是InnoDB,根据InnoDB索引的结构,查询过程为:

  • 通过二级索引查到主键值(找出所有gender=1的id)。

  • 再根据查到的主键值通过主键索引找到相应的数据块(根据id找出对应的数据块内容)。

  • 根据offset的值,查询300001次主键索引的数据,最后将之前的300000条丢弃,取出最后1条。

不过既然二级索引已经找到主键值,为什么还需要先用主键索引找到数据块,再根据offset的值做偏移处理呢?

如果在找到主键索引后,先执行offset偏移处理,跳过300000条,再通过第300001条记录的主键索引去读取数据块,这样就能提高效率了。

如果我们只查询出主键,看看有什么不同

mysql> select id from member where gender=1 limit 300000,1;
+--------+| id     |
+--------+| 599465 |
+--------+1 row in set (0.09 sec)

很明显,如果只查询主键,执行效率对比查询全部字段,有很大的提升。

推测

只查询主键的情况
因为二级索引已经找到主键值,而查询只需要读取主键,因此mysql会先执行offset偏移操作,再根据后面的主键索引读取数据块。

需要查询所有字段的情况
因为二级索引只找到主键值,但其他字段的值需要读取数据块才能获取。因此mysql会先读出数据块内容,再执行offset偏移操作,最后丢弃前面需要跳过的数据,返回后面的数据。

证实

InnoDB中有buffer pool,存放最近访问过的数据页,包括数据页和索引页。

为了测试,先把mysql重启,重启后查看buffer pool的内容。

mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('primary','gender') and TABLE_NAME like '%member%' group by index_name;
Empty set (0.04 sec)

可以看到,重启后,没有访问过任何的数据页。

查询所有字段,再查看buffer pool的内容

mysql> select * from member where gender=1 limit 300000,1;
+--------+------------+--------+| id     | name       | gender |
+--------+------------+--------+| 599465 | f48375bdb8 |      1 |
+--------+------------+--------+1 row in set (0.38 sec)mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('primary','gender') and TABLE_NAME like '%member%' group by index_name;
+------------+----------+| index_name | count(*) |
+------------+----------+| gender     |      261 || PRIMARY    |     1385 |
+------------+----------+2 rows in set (0.06 sec)

可以看出,此时buffer pool中关于member表有1385个数据页,261个索引页。

重启mysql清空buffer pool,继续测试只查询主键

mysql> select id from member where gender=1 limit 300000,1;
+--------+| id     |
+--------+| 599465 |
+--------+1 row in set (0.08 sec)mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('primary','gender') and TABLE_NAME like '%member%' group by index_name;
+------------+----------+| index_name | count(*) |
+------------+----------+| gender     |      263 || PRIMARY    |       13 |
+------------+----------+2 rows in set (0.04 sec)

可以看出,此时buffer pool中关于member表只有13个数据页,263个索引页。因此减少了多次通过主键索引访问数据块的I/O操作,提高执行效率。

因此可以证实,mysql查询时,offset过大影响性能的原因是多次通过主键索引访问数据块的I/O操作。(注意,只有InnoDB有这个问题,而MYISAM索引结构与InnoDB不同,二级索引都是直接指向数据块的,因此没有此问题 )。

InnoDB与MyISAM引擎索引结构对比图

这里写图片描述


优化方法

根据上面的分析,我们知道查询所有字段会导致主键索引多次访问数据块造成的I/O操作。

因此我们先查出偏移后的主键,再根据主键索引查询数据块的所有内容

分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • 分享下mysql各个主要版本之间的差异
  • MySQL essential版本和普通版本有什么区别?
  • redhat 5.4下安装MYSQL全过程
  • 如何用SQL命令查看Mysql数据库大小
  • 解析mysql中如何获得数据库的大小
  • 解析mysql修改为utf8后仍然有乱码的问题
  • 5个常用的MySQL数据库管理工具详细介绍
  • 解析在MySQL里创建外键时ERROR 1005的解决办法
  • 解析远程连接管理其他机器上的MYSQL数据库
  • mysql 精简过程(删除一些文件)

相关文章

  • 2018-12-05SQL Server 交叉表查询 case
  • 2018-12-05windows下安装、卸载mysql服务的方法(mysql 5.6 zip解压版安装教程)_MySQL
  • 2018-12-05MySQL交叉表实现分享
  • 2017-05-11浅谈开启magic_quote_gpc后的sql注入攻击与防范
  • 2018-12-05Mysql自动备份与还原方法
  • 2018-12-05MS-sql 2005拒绝了对对象 ''xxx'' (数据库 ''xxx'',架
  • 2017-05-11完全卸载mysql(停止服务、卸载相关程序、删除注册表
  • 2018-12-05数据库Left join , Right Join, Inner Join 的相关内容,非常实
  • 2018-12-05关于生产库中遇到mysql的子查询示例详解
  • 2017-05-11mysql中Load Data记录换行问题的解决方法

文章分类

  • MsSql
  • Mysql
  • oracle
  • MariaDB
  • DB2
  • SQLite
  • PostgreSQL
  • MongoDB
  • Redis
  • Access
  • 数据库其它
  • sybase
  • HBase

最近更新的内容

    • mysql实现SQL统计的实例
    • mysql启动服务报1058错误的解决方法
    • sql 游标使用笔记
    • Oracle也有注入漏洞
    • 正解mysql: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by /usr/lib64/libstdc++.so.6)
    • MongoDB循序渐进之[特性]介绍
    • Oracle RAC配置ssh用户等价
    • 深入Mysql字符集设置 图文版
    • 关于增删改操作课程汇总
    • Mysql数据库错误代码中文详细说明

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有