一、分页

  1. 什么是分页

    一般在客户端实现分页功能的时候,要显示当前页的数据、当前所在页数、临近页面的按钮以及总页数等等。这些数据随着翻页的进行能够动态的变化,为了实现这样的效果,一般会采取两种办法:真分页和假分页。这样的划分方式是从与数据库的交互方式出发的,是每次翻页时都进行查询还是一次性查出所有的数据。

  2. 真分页

    真分页指的是每次在进行翻页时都只查询出当前页面的数据,特点就是与数据库的交互次数较多,但是每次查询的数据量较少,数据也不需要一直保存在内存中。适用于数据量比较大的场景,数据不适合全量查出的情况。

  1. 假分页假分页指的是对于要显示的数据一次性全部查出,一直存在在服务端或客户端,在前端进行分页或由服务端控制分页。将根据当前所在页来计算应该显示的数据所在下标,用循环取出目标数据。只有当会话断开或页面关闭,相应的资源才会被释放。
  1. 缓存层真分页和假分页都要和数据库进行交互,对于真分页来说不需要担心数据同步的问题,因为每次都是查询出最新的,但是数据库的负担会很重,尤其是用户量大的情况下。假分页可以在一定程度上减轻数据库的压力,但是数据不能及时得到同步,除非重新请求或页面刷新。一般在企业中会有缓存层的存在,既能有效降低数据库的压力,又能及时的进行数据同步。在对数据库中的数据进行修改后,要将变更后的数据及时同步到缓存层,在进行数据查询时从缓存层获取。

二、MySQL实现分页

  1. LIMIT用法

    LIMIT出现在查询语句的最后,可以使用一个参数或两个参数来限制取出的数据。其中第一个参数代表偏移量:offset(可选参数),第二个参数代表取出的数据条数:rows。

  • 单参数用法

    当指定一个参数时,默认省略了偏移量,即偏移量为0,从第一行数据开始取,一共取rows条。

1
2
/* 查询前5条数据 */
SELECT * FROM Student LIMIT 5;
  • 双参数用法

    当指定两个参数时,需要注意偏移量的取值是从0开始的,此时可以有两种写法:

1
2
3
4
/* 查询第1-10条数据 */
SELECT * FROM Student LIMIT 0,10;
/* 查询第11-20条数据 */
SELECT * FROM Student LIMIT 10 OFFSET 10;
  1. 分页公式
  • 总页数计算

    在进行分页之前,我们需要先根据数据总量来得出总页数,这需要用到COUNT函数和向上取整函数CEIL,SQL如下:

1
2
3
4
/* 获得数据总条数 */
SELECT COUNT(*) FROM Student;
/* 假设每页显示10条,则直接进行除法运算,然后向上取整 */
SELECT CEIL(COUNT(*) / 10) AS pageTotal FROM Student;

核心信息

当前页:pageNumber

每页数据量:pageSize

据此我们可以总结出,LIMIT所需要的两个参数计算公式如下:

1
2
offset:(pageNumber - 1) * pageSize
rows:pageSize

三、查询优化

但是上述查询在偏移量特别大的情况下,可能会出现效率问题

下面是一个实例:

1
select * from orders_history where type=8 limit 1000,10;

该条语句将会从表 orders_history 中查询第1000条数据之后的10条数据,也就是第1001条到第1010条数据。

数据表中的记录默认使用主键(一般为id)排序,上面的结果相当于:

1
select * from orders_history where type=8 order by id limit 10000,10;

此处缺少实验数据,以后补充

这种分页查询方式会从数据库第一条记录开始扫描,所以越往后,查询速度越慢,而且查询的数据越多,也会拖慢总查询速度。

1.使用子查询优化

这种方式先定位偏移位置的 id,然后往后查询,这种方式适用于 id 递增的情况

1
2
3
4
5
6
7
8
9
select * from orders_history where type=8 limit 100000,1;

select id from orders_history where type=8 limit 100000,1;

select * from orders_history where type=8 and
id>=(select id from orders_history where type=8 limit 100000,1)
limit 100;

select * from orders_history where type=8 limit 100000,100;

2.使用 id 限定优化

这种方式假设数据表的id是连续递增的,则我们根据查询的页数和查询的记录数可以算出查询的id的范围,可以使用 id between and 来查询:

1
2
select * from orders_history where type=2 
and id between 1000000 and 1000100 limit 100;

这种查询方式能够极大地优化查询速度,基本能够在几十毫秒之内完成。限制是只能使用于明确知道id的情况,如果表用的是分布式 id (如:UUID、雪花算法),则不适用。