咱们常常会上一些小说网站看小说,他们老是会把最近更新了的小说放在最前面,也就是第一页,而好久没更新的就放在后面了。那么这样网站是如何实现的呢?个人猜测有一张小说表,表里面一个last_update表示最后更新字段,而且对这个字段创建了索引,按last_update从大到小排序以后再按需求的页面大小进行分页。java
那为何分页查询老是要排序呢?mysql
由上面的例子能够看出,这个是需求致使的,并且还有效率因素。下面举例说明:sql
表的描述以下:数据结构
只有id主键索引。优化
假设咱们须要前三条数据,执行sql【explain select * from t1 limit 3;】网站
由于咱们没有进行排序,mysql不知道要哪前三条,是id大呢,还name大呢?,mysql晕了,只能进行全表扫描来获取数据表的前三条数据了。因此范围类型为ALL。.net
当咱们进行排序以后状况如何。执行sql【explain select * from t1 order by id desc limit 3;】3d
能够看到,查询类型为索引扫描,可是rows字段显示咱们最多访问三条数据,因此sql效率挺高。固然,前提是咱们的排序用上了相关的索引,不然的话偷鸡不成蚀把米,弄出一个using filesort。blog
为何不能使用limit的offset?排序
其实offset是对咱们偏移的数据进行遍历,咱们偏移多少mysql就遍历多少,若是一个offset=1000000,那这条sql就至少要扫描1000000条数据了。
执行sql【explain select * from t1 order by id desc limit 3 offset 15;】
能够看到,访问类型依然是索引扫描,可是rows却变成了18,比以前的3可大多了。
因此千万别用offset,咱们可使用查询来筛选出须要的字段,好比:
能够把上面的sql转化为【select * from t1 where id<=5 and id>=3 order by id desc limit 3】
能够看出查出的数据彻底同样,那么下面的sql效率如何呢?
执行sql【explain select * from t1 where id<=5 and id>=3 order by id desc limit 3;】
优化很是明显,访问类型变成了range,rows变成了3。
通常来讲咱们会弄一个javabean记录查询信息,好比说,当前页数,id最大值,id最小值,须要到的页数。
假设当前页面大小为3,页面id最大值为17,最小值为15。
除了上面第二节讲的让id在一个范围内查询的方法,咱们也可使用下面的方法。
那么下一页就是【select * from t1 where id<15 order by id desc limit 3;】
虽然rows为14,但并不会扫描这么多,获得3条就停,由于rows是一个最大估计值。
上一页就是【select * from t1 where id>17 order by id desc limit 3;】
有时须要进行手动输入页数,好比直接从第1页跳到666页,对于实现跳页功能这个就必需要获取整张表到底有多少数据行了。可是又不能 count(*) 的代价过大,由于mysql必需要全扫描来统计行数,那么 count(*) 如何优化呢?这里就再也不重复讲解了,能够参考个人这篇文章【MySQL系列-优化之count()】。
ok,假设你已经看了个人 count() 优化的文章,同时数据表变成了这样。
下面在继续分析。
由于咱们是按id从大到小进行排序,因此为了实现跳页,咱们必须知道用户当前在第几页,以及页面的大小。假设用户如今在第5页,页面大小为3,那么页面的最小值为20-3*5+1=6,页面最大值为6+3-1=8。若是如今用户想去第2页,第二页的页面最小值为20-2*3+1=15,页面最大值为15+3-1=17。那么sql就很容易能够写来。
第一步获取总数【select count(*) from t1;】
第二步执行需求页面的查询【select * from t1 where id<=17 and id>=15 order by id desc limit 3;】
固然也能够写成一个嵌套子查询。
当用户在跳页查询的时候有数据插入咋办?直接进行锁表吗。
若是id不是连续而是中间有缺值呢?不少网站是没有提供跳页功能,可是应该有更高级的方法来实现,好比创建复杂的数据结构来维护之类的。