MySQL百万级数据大分页查询优化的实现
Posted 业余砖家
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL百万级数据大分页查询优化的实现相关的知识,希望对你有一定的参考价值。
前言:在数据库开发过程中我们经常会使用分页,核心技术是使用用limit start, count分页语句进行数据的读取。
一、MySQL分页起点越大查询速度越慢
直接用limit start, count分页语句,表示从第start条记录开始选择count条记录 :
select * from test limit start, count ;
当起始页较小时,查询没有性能问题,我们分别看下从10, 1000, 10000, 100000开始分页的执行时间(每页取20条)。
select * from test limit 10, 20 ; //0.002秒 select * from test limit 1000, 20 ; //0.011秒 select * from test limit 10000, 20 ; //0.027秒 select * from test limit 100000, 20 ; //0.057秒
我们已经看出随着起始记录的增加,时间也随着增大, 这说明分页语句limit跟起始页码是有很大关系的,那么我们把起始记录改为100w看下:
select * from test limit 1000000, 20 ; //0.682秒
我们惊讶的发现MySQL在数据量大的情况下分页起点越大查询速度越慢,300万条起的查询速度已经需要1.368秒钟。这是为什么呢?
因为limit 3000000,10的语法实际上是MySQL扫描到前3000020条数据,之后丢弃前面的3000000行,这个步骤其实是浪费掉的。
select * from test limit 3000000, 20 ; //1.368秒
从中我们也能总结出两件事情:
(1) limit语句的查询时间与起始记录的位置成正比
(2) MySQL的limit语句是很方便,但是对记录很多的表并不适合直接使用。
二、 limit大分页问题的性能优化方法
(1)利用表的覆盖索引来加速分页查询
MySQL的查询完全命中索引的时候,称为覆盖索引,是非常快的。因为查询只需要在索引上进行查找之后可以直接返回,而不用再回表拿数据。
在我们的例子中,我们知道id字段是主键,自然就包含了默认的主键索引。现在让我们看看利用覆盖索引的查询效果如何。
select id from test limit 1000000, 20 ; //0.2秒
那么如果我们也要查询所有列,如何优化?
优化的关键是要做到让MySQL每次只扫描20条记录,我们可以使用limit n,这样性能就没有问题,因为MySQL只扫描n行。我们可以先通过子查询先获取起始记录的id,然后根据Id拿数据:
select * from test where id>=(select id from test limit 1000000,1) limit 20;
(2)用上次分页的最大id优化
先找到上次分页的最大ID,然后利用id上的索引来查询,类似于:
select * from user where id>1000000 limit 100 ;
(3)延迟关联
和上述的子查询做法类似,我们可以使用JOIN,先在索引列上完成分页操作,然后再回表获取所需要的列。
select a.* from test a inner join (select id from test limit 1000000, 10) b on a.id=b.id;
本文来自博客园,作者:业余砖家,转载请注明原文链接:https://www.cnblogs.com/yeyuzhuanjia/p/17466464.html
mysql百万级数据分页查询缓慢优化方法
参考网址1:https://www.cnblogs.com/nightOfStreet/p/9647926.html -------------修改需求
一、与产品商讨 修改需求
- 后端工程师,在接到分页list需求的时候,内心是这样的
- 画面是这样的
- 代码大概是这样的
- select count(id) from … 查出总数
- select * from …. limit m,n 查出分页数据
- 和前端的交互是这样的
- 你传给我查询条件和pageNum、pageSize
- 我给你返回total、resultList
- 恩,可以开始预估开发时间了
- 结果出来的时候是这样的
- 在点击后面几页的时候发现数据加载很慢。不好,一开始没想到数据量是这个级别的,慢SQL监控已经开始预警
- 定位到原因有两个
- select count(*) 总量会扫全表
- limit m,n 当偏移量m很大的时候,查询每次都要先扫到m条满足条件的记录
- 其他人一定也遇到过这个问题,打开百度还没开始搜索就找到了一个解决方案,百度是这样做的
- (正解)我去,这不是耍流氓吗
- 没有总数,就不需要count,第一个问题解决
- limit m,n 百度出来的解决方案都是加一个唯一标识去记录上一次查询的记录在哪里
- 百度这种做法是搜索引擎自带的分页,不过mysql分页同样可以借鉴,找来产品大大沟通一发
- “面对这么多的数据,只要把新的数据放到前面,用户不会care后面的数据,是不是可以把页码干掉”
- “ 用户也不会管我们到底有多少条记录,来这里也只是做检索而已,总数也可以去掉的,如果要统计数据量我们再单独统计”
- 。。。理由还可以有很多,产品大大还是很好说话的
- (正解)我去,这不是耍流氓吗
- 最后设计出来是这样的
-
代码实现是这样的
-
id是自增主键
-
select * from … where id > y order by id limit n (向下翻页)
- select * from … where id < y order by id desc limit n (向上翻页)
-
- 和前端的交互是这样的
- 你告诉我是向上翻页还是向下翻页,给我当前页第一条(上一页)或者最后一条(下一页)记录的id
- 我给你数据数据
-
- 总结
- 重点是在传统分页方案上遇到的两个问题
- select count(*) 总量会扫全表
- limit m,n 当偏移量m很大的时候,查询每次都要先扫到m条满足条件的记录
- 解决方案是产品和技术共同确认的结果,如果产品不接受,此方案不可行
- 实际开发中还会碰到的问题
- 上一页/下一页、没有数据的处理方式
- 查询自身带有排序条件,需要order by多字段
- 重点是在传统分页方案上遇到的两个问题
参考网址2:https://www.cnblogs.com/icebutterfly/p/9199202.html
二、优化sql查询语句
1.子查询优化法
先找出第一条数据,然后大于等于这条数据的id就是要获取的数据
缺点:数据必须是连续的,可以说不能有where条件,where条件会筛选数据,导致数据失去连续性
实验下
mysql> set profi=1;
Query OK, 0 rows affected (0.00 sec)
mysql> select count(*) from Member;
+———-+
| count(*) |
+———-+
| 169566 |
+———-+
1 row in set (0.00 sec)
mysql> pager grep !~-
PAGER set to ‘grep !~-‘
mysql> select * from Member limit 10, 100;
100 rows in set (0.00 sec)
mysql> select * from Member where MemberID >= (select MemberID from Member limit 10,1) limit 100;
100 rows in set (0.00 sec)
mysql> select * from Member limit 1000, 100;
100 rows in set (0.01 sec)
mysql> select * from Member where MemberID >= (select MemberID from Member limit 1000,1) limit 100;
100 rows in set (0.00 sec)
mysql> select * from Member limit 100000, 100;
100 rows in set (0.10 sec)
mysql> select * from Member where MemberID >= (select MemberID from Member limit 100000,1) limit 100;
2.倒排表优化法
倒排表法类似建立索引,用一张表来维护页数,然后通过高效的连接得到数据
缺点:只适合数据数固定的情况,数据不能删除,维护页表困难
3.反向查找优化法
当偏移超过一半记录数的时候,先用排序,这样偏移就反转了
缺点:order by优化比较麻烦,要增加索引,索引影响数据的修改效率,并且要知道总记录数
,偏移大于数据的一半
引用
limit偏移算法:
正向查找: (当前页 – 1) * 页长度
反向查找: 总记录 – 当前页 * 页长度
以上是关于MySQL百万级数据大分页查询优化的实现的主要内容,如果未能解决你的问题,请参考以下文章