MySQL分页性能思考
MySQL分页性能思考
关键词:深度分页
背景
最近有一个需求:在后台管理页面中,需要展示产品信息的列表。
之前版本开发中产品信息是用户填写完所有字段之后能进行保存。在之前的基础上需要支持用户不完全填写字段进行展示和保存的功能。
一个很简单的想法是为空也直接保存就可以了,但是由于之前的开发中MySQL中已经约束了字段NOT NULL,担心修改表结构引发一些意料之外的错误。
因此考虑使用Redis作为临时保存信息的能力,避免直接修改MySQL的数据表格式,同时一般设置来说Redis也只会丢失1s的数据,可以接受这个丢失。
使用Redis之后,如何在列表页展示产品信息呢?这个简单问题引发了我的一些思考。
mysql怎么做分页,有哪几种方案
简单和通用的做法:offset+limit,sql语句大概是:
select * from my_table where my_table.name = 'oneName' order by my_table.id limit 100000,10;
对于offset+limit的方案,其会在offset及大的时候性能变慢:10w的offset:0.742s,0的offset为0.006s。
性能低的原因在于:大量无意义的回表查询(回表总次数为offset+limit的值),丢弃了大量数据(offset),只保留了很少部分(limit) 。like:
- 通过普通二级索引树idx_update_time,过滤update_time条件,找到满足条件的记录ID。
- 通过ID,回到主键索引树,找到满足记录的行,然后取出展示的列(回表)
- 扫描满足条件的100010行,然后扔掉前100000行,返回。
其执行计划:
因此我们的优化思路在于:减少无意义的回表查询。
有三种方法:
使用子查询提前筛选出id,避免无意义的回表和筛选。在下面的例子中,就使用子查询先筛选出
a.id,避免大量回表。耗时约为0.038,由于有了子查询,耗时肯定是比offset为0要高的。语句like:select id,name,balance FROM account where id >= (select a.id from account a where a.update_time >= '2020-09-19' limit 100000, 1) LIMIT 10;(可以加下时间条件到外面的主查询)
使用
inner join同一张表,原理其实和子查询是一样的。语句like:SELECT acct1.id,acct1.name,acct1.balance FROM account acct1 INNER JOIN (SELECT a.id FROM account a WHERE a.update_time >= '2020-09-19' ORDER BY a.update_time LIMIT 100000, 10) AS acct2 on acct1.id= acct2.id;
容易混淆:上面两个语句都用到了
SELECT a.id FROM account a WHERE a.update_time >= '2020-09-19' ORDER BY a.update_time LIMIT 100000, 10,但是需要注意,查询的是a.id,走的索引,并不会回表哦。
游标法:使用主键id作为游标,同样可以避免大量无意义的回表,语句范例如下。
select id,name,balance FROM account where id > 100000 order by id limit 10;
值得一提的是,对于上面三种优化,都只提及了id作为筛选项目,如果涉及更多where条件之类的,优化思路都是类似的,即:减少无意义的回表查询。
MySQL和Redis一起之后如何分页
在使用REDIS保存用户没有完全填写的产品信息之后,列表展示页面我们需要同时展示REDIS中保存的不完全填写的数据和MySQL中完全填写的数据。
对于列表页这样瀑布流展示的数据,在展示的时候我们需要合并MySQL和REDIS中的数据,并且尽量保持原有逻辑,按照产品id降序排列,如何做呢?
补充:REDIS保存的数据是用的String类型,key就是产品的id,value为产品信息序列化后的字符串。又使用了一个Zset来保存Redis中保存的产品id。
在考虑混合Redis中的数据之后,相比于只用考虑MySQL的的数据,难点在于分页非常不好做,常规的limit+offset方案的性能让人无法接收!
为什么性能差
我们原有的查询中,一般没有考虑深度分页的问题,都是直接limit+offset进行查询。
在混合进Redis的数据之后,数据分布可能如下图所示:
数据在Redis中和MySQL中并没有一个固定的顺序,而是交错的,在这样的情况下暴力的limit+offset的方案就会导致更多的无效查询,比如:如果需要查询每页50条数据,当前处于第30页。如果只考虑MySQL的查询,只用limit 30*50,50即可;现在同时考虑MySQL和Redis的数据查询,需要通过以下步骤:
- 第一步需要首先通过
limit 0,30*50+50塞选出MySQL中的产品ID; - 第二步取出Redis的zset中保存的所有的产品ID;
- 第三步在业务代码中按照顺序重新降序组装出本次展示所需要的产品ID;
- 第四步并再回到MySQL和REIDS中查询并组装展示;
上面第一步中直接全部取出产品信息会进一步增加耗时,这里已经算是优化后的步骤了。
上面的步骤看起来非常的“没有必要”,我们逐个解释一下:
- 第一步:第一步中为啥offset中要用
30*50+50取这么多产品ID,分明这次只需要50个数据?原因就在于混入Redis的数据之后,全局排名(偏移量)30*50的数据,在MySQL中排名(偏移量)的范围是[0,30*50],所以必须全部取出参与排序。 - 第二步:第二部中为啥要取出Redis中保存的所有的产品ID?原因在于Redis中zset虽然支持按照分数范围进行筛选,但是并不支持按照偏移量进行筛选,所以只能全部取出
- 第三步,第四步:很正常的步骤。
怎么优化
性能这么差,简直不能忍受!所以我们上面MySQL做分页的优化可以排上用场了。MySQL深度分页的几个优化思路(子查询、inner join、游标)中游标优化可以解决我们的问题,子查询和inner joint并不能解决我们的问题。
因为使用游标优化之后,无论是MySQL中取数据还是Redis中取数据我们都可以直接用游标来限制取数据的范围了:MySQL中可以用where xxx>? limit 0,50;Redis中zset中zrange取数据的时候也可以限制范围了。而剩下的两种优化方法本质上我们举得案例中就已经采用了,对这种多数据源合并的情况虽然可能有一些提升,但并没有解决本质问题。 不过改用游标法之后,我们需要前端同学配合我们修改,将原先的offset+limit的方案迁移到游标方案来。
实际上Redis来保存仍然有一些点需要考虑,比如说随着产品ID增多,会产生zset的bigkey问题,或者是产品本身信息过多带来的bigkey问题等等。
此外,我们也可以考虑一些代码之外的方案,比如说和业务商量一下是否可以不保证按照产品ID递减的方式来展示,可以先展示完缓存的数据等等~
参考:
mysql查询 limit 1000,10 和limit 10 速度一样快吗?如果我要分页,我该怎么办?-腾讯云开发者社区-腾讯云
更推荐看这个 实战!聊聊如何解决MySQL深分页问题
MySQL分页性能思考的更多相关文章
- mysql 分页性能优化
最简单的分页方法是这样的 , 该表中存在5w左右数据 执行时间平均在10s左右,因此该种方式在数据量大的情况下查询效率极低. 优化方式有以下几种 1.此种方式平均在7-8s之间(CreateDate ...
- mysql分页性能
- select * from userinfo limit 20000,10 # 数据越往后越慢 - 索引表中扫: select * from userinfo where id in (sele ...
- MySQL— 索引,视图,触发器,函数,存储过程,执行计划,慢日志,分页性能
一.索引,分页性能,执行计划,慢日志 (1)索引的种类,创建语句,名词补充(最左前缀匹配,覆盖索引,索引合并,局部索引等): import sys # http://www.cnblogs.com/w ...
- mysql 分页offset过大性能问题解决思路
在公司干活一般使用sqlserver数据库.rownumber分页贼好用. 但是晚上下班搞自己的事情就不用sqlserver了.原因就是自己的渣渣1核2g的小服务器完全扛不住sqlserver那么大的 ...
- day05 mysql pymysql的使用 (前端+flask+pymysql的使用) 索引 解释执行 慢日志 分页性能方案
day05 mysql pymysql 一.pymysql的操作 commit(): 在数据库里增删改的时候,必须要进行提交,否则插入的数据不生效 1.增, 删, 改 #co ...
- MySQL分页查询的性能优化
MySQL limit分页查询的性能优化 Mysql的分页查询十分简单,但是当数据量大的时候一般的分页就吃不消了. 传统分页查询:SELECT c1,c2,cn… FROM table LIMIT n ...
- mysql数据库性能优化(包括SQL,表结构,索引,缓存)
优化目标减少 IO 次数IO永远是数据库最容易瓶颈的地方,这是由数据库的职责所决定的,大部分数据库操作中超过90%的时间都是 IO 操作所占用的,减少 IO 次数是 SQL 优化中需要第一优先考虑,当 ...
- Mysql 分页语句Limit用法
转载自:http://qimo601.iteye.com/blog/1634748 1.Mysql的limit用法 在我们使用查询语句的时候,经常要返回前几条或者中间某几行数据,这个时候怎么办呢?不用 ...
- 第6章 影响 MySQL Server 性能的相关因素
前言: 大部分人都一致认为一个数据库应用系统(这里的数据库应用系统概指所有使用数据库的系统)的性能瓶颈最容易出现在数据的操作方面,而数据库应用系统的大部分数据操作都是通过数据库管理软件所提供的相关接口 ...
- MySQL分页优化中的“INNER JOIN方式优化分页算法”到底在什么情况下会生效?
本文出处:http://www.cnblogs.com/wy123/p/7003157.html 最近无意间看到一个MySQL分页优化的测试案例,并没有非常具体地说明测试场景的情况下,给出了一种经典的 ...
随机推荐
- 『玩转Streamlit』--图像与媒体组件
Streamlit中的图像与媒体组件,主要是st.image.st.audio和st.video. 它们是专为在Streamlit Web应用程序中嵌入和展示多媒体内容而设计的,这些组件不仅丰富了应用 ...
- ARC134C The Majority
ARC134C The Majority link:[ARC134C] The Majority 小清新数学题.(反正我做不出来) 简要题意 有\(K\)个箱子,编号为\(1\)到\(K\)的箱子.起 ...
- ES6 延展操作符
延展操作符(Spread operator) 延展操作符 = ...可以在函数调用/数组构造时,将数组表达式或者string在语法层面展开,还可以在构造对象时,将对象表达式按key-value的方式展 ...
- bootstrap table 搜索只从当前页开始搜
项目中出现的情况,使用bootstrap table框架,使用搜索功能的是后查询的结果不是从第一也开始,有时候点击搜索第一次查不出来结果,点击第二次结果才出现. 解决方法: $("#btn_ ...
- 网站安全锁-SSL证书
为了安全起见,现在开发微信服务号和IOS客户端等访问服务器端都要求使用https加密传输. SSL证书是数字证书的一种,类似于驾驶证.护照和营业执照的电子副本.因为配置在服务器上,也称为SSL服务器证 ...
- Mock.js 笔记
1.介绍 官方文档,Mock.js用来生成随机数据,拦截 Ajax 请求 支持的数据类型丰富,包括文本.数字.布尔值.日期.邮箱.链接.图片.颜色等 功能灵活,生成随机数据 和 拦截 Ajax 请求 ...
- 规范LR(1)语法分析表
前面已经实现了SLR语法分析表,但是可能会出现即使语法不是二义性文法,也存在移入/规约冲突 状态 i 包含项[A ->α ],当状态 i 出现在栈顶时,栈中的可行前缀时βα且在任何最后句型中a都 ...
- ProWindow的控制按钮状态
在使用ArcGIS.Desktop.Framework.Controls.ProWindow时,有网友发现,设置窗口调整模式后,窗口的控制按钮是不全的. ProWindow win = new Pro ...
- HBuilderX代码缩进问题
前情 uni-app是我很喜欢的跨平台框架,它能开发小程序,H5,APP(安卓/iOS),对前端开发很友好,自带的IDE让开发体验也很棒,公司项目就是主推uni-app,自然也是用官方自带的IDE了 ...
- R数据分析:国产新冠口服药比辉瑞好的文章的统计做法分享
元旦前在人民日报中央厨房上看到一篇文章,叫做"比肩辉瑞的国产新冠药物VV116,是这样研制和临床试验的",想来就把文献原文找来读了读,写下本文分享给大家,本文主要关注文章的正文中主 ...