不少的朋友在面试中会遇到这样的问题,也有不少的项目在运营一段时间后也会遇到MYSQL查询中变慢的一些瓶颈,今天这儿简单的介绍下我经常使用的几种查询分页的方法,我所知道的也无非就是索引、分表、子查询偏移,因此要是有什么不对或有更好的方法,欢迎你们留言讨论。

效率分析关键词:explain + SQL语句

一,最多见MYSQL最基本的分页方式limit:

select * from `table` order by id desc limit 0, 20

在中小数据量的状况下,这样的SQL足够用了,惟一须要注意的问题就是确保使用了索引。随着数据量的增长,页数会愈来愈多,在数据慢慢增加的过程当中,可能就会出现limit 10000,20这样的状况,limit 10000,20的意思扫描知足条件的10020行,扔掉前面的10000行,返回最后的20行,问题就在这里,若是是limit 100000,100,须要扫描100100行,在一个高并发的应用里,每次查询须要扫描超过10W行,性能确定大打折扣。

这种方式有几个不足:较大的偏移(OFFSET)会增长结果集,小比例的低效分页足够产生磁盘I/O瓶颈,须要扫描的行多。

简单的解决方法:不显示记录总数,没用户在意这个数字;不让用户访问页数比较大的记录,重定向他们;避免count(*) ,不显示总数,让用户经过“下一页”来翻页 ,缓存总数;单独统计总数,在插入和删除时递增/递减

二,第二种就是分表,计算HASH值,这儿不作介绍了,我目前也没有在项目中真正使用过这种方法,还停留在理论层次;

三,第三种是偏移:

SELECT * FROM `table` WHERE id <= (SELECT id FROM `table` ORDER BY id desc LIMIT ".($page-1)*$pagesize.", 1) ORDER BY id desc LIMIT $pagesize

或者

select * FROM `table` AS t1 JOIN (SELECT id FROM `table` ORDER BY id desc LIMIT 900,1) AS t2 WHERE t1.id<=t2.id order by t1.id desc limit 5

原理就是记录住当前页id的最大值和最小值,计算跳转页面和当前页相对偏移,因为页面相近,这个偏移量不会很大,这样的话m值相对较小,大大减小扫 描的行数。其实传统的limit m,n,相对的偏移一直是第一页,这样的话越翻到后面,效率越差,而上面给出的方法就没有这样的问题。

好比仍是SELECT * FROM `table` ORDER BY id DESC,按id降序分页,每页20条,当前是第10页,当前页条目id最大的是9527,最小的是9500,若是咱们只提供”上一页”、”下一页”这样 的跳转(不提供到第N页的跳转),那么在处理”上一页”的时候SQL语句能够是:

SELECT * FROM `table` WHERE id > 9527 ORDER BY id ASC LIMIT 20;

处理”下一页”的时候SQL语句能够是:

SELECT * FROM `table` WHERE id < 9500 ORDER BY id DESC LIMIT 20;

无论翻多少页,每次查询只扫描20行。

缺点是只能提供”上一页”、”下一页”的连接形式,可是我通常来讲很是喜欢””这样的连接方式,怎么办呢?

若是LIMIT m,n不可避免的话,要优化效率,只有尽量的让m小一下,咱们扩展前面作法,仍是SELECT * FROM `table` ORDER BY id DESC,按id降序分页,每页20条,当前是第10页,当前页条目id最大的是9527,最小的是9500,好比要跳到第8页,我看的SQL语句能够这 样写:

SELECT * FROM `table` WHERE id > 9527 ORDER BY id ASC LIMIT 20,20;

跳转到第13页:

SELECT * FROM `table` WHERE id < 9500 ORDER BY id DESC LIMIT 40,20;

注意SQL语句里面的ASC和DESC,若是是ASC取出来的结果,显示的时候记得倒置一下。

总体来讲在面对百万级数据的时候若是使用上面第三种方法来优化,系统性能上是可以获得很好的提高,在遇到复杂的查询时也尽可能简化,减小运算量。 同时也尽可能多的使用内存缓存,有条件的能够考虑分表、分库、阵列之类的大型解决方案了。