千家信息网

mysql查询时offset过大影响性能的原因是什么

发表于:2025-01-20 作者:千家信息网编辑
千家信息网最后更新 2025年01月20日,这篇文章主要介绍了mysql查询时offset过大影响性能的原因是什么,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。准备测试数据表及数
千家信息网最后更新 2025年01月20日mysql查询时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条记录

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操作。

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

mysql> select a.* from member as a inner join (select id from member where gender=1 limit 300000,1) as b on a.id=b.id;+--------+------------+--------+| id  | name  | gender |+--------+------------+--------+| 599465 | f48375bdb8 |  1 |+--------+------------+--------+1 row in set (0.08 sec)

附:MYSQL limit,offset 区别

SELECT  keywordFROM  keyword_rankWHERE  advertiserid='59'order by  keywordLIMIT 2 OFFSET 1;

比如这个SQL ,limit后面跟的是2条数据,offset后面是从第1条开始读取

SELECT  keywordFROM  keyword_rankWHERE  advertiserid='59'ORDER BY  keywordLIMIT 2 ,1;

而这个SQL,limit后面是从第2条开始读,读取1条信息。

这两个千万别搞混哦。

感谢你能够认真阅读完这篇文章,希望小编分享的"mysql查询时offset过大影响性能的原因是什么"这篇文章对大家有帮助,同时也希望大家多多支持,关注行业资讯频道,更多相关知识等着你来学习!

数据 索引 查询 效率 原因 性能 影响 内容 字段 偏移 情况 篇文章 结构 问题 分析 测试 不同 很大 只有 数据表 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 无锡信息网络技术服务 软件开发简历格式模板下载 区块链的数据存在数据库 预防犯罪网络安全 山东计算机网络技术课本 软件开发的类型系数 鬼泣巅峰之战张大仙在哪个服务器 多项目管理软件开发 诺安互联网科技基金净值 网络安全第一课开讲了 郴州网络安全和信息化委员会 db数据库查看工具完整正式版 什么叫网络安全类型不安全 广电网络安全对抗赛浙江 哪里有高性价比的服务器卖 我国金融信用信息基础数据库运行 数据库系统概论检索 数据库描述怎么写 软件开发与管理技术专业 服务器远程登陆密码 世界通信服务器有几台 信息与网络安全管理制度 机房服务器都有哪些设备 诺安互联网科技基金净值 普陀区智能网络技术特点 北京楼控管理软件开发定制 怎么用java开发服务器 广电网络安全对抗赛浙江 大学里学习网络技术有用吗 社会各界监督网络安全
0