当需要从数据库查询的表有上万条记录的时候,一次性查询所有结果会变得很慢,特别是随着数据量的增加特别明显,于是需要使用分页查询。

1 一般分页查询

一般的分页查询使用 limit 子句实现:

select * from table_name LIMIT [offset,] row | rows OFFSET offset;

limit 子句可以用于指定 select 语句返回的记录数。
注意:

  1. 第一个参数指定第一个返回的记录行的偏移量,从 0 开始;
  2. 第二个参数指定返回记录行的最大数目;
  3. 如果只给定一个参数:它表示返回最大的行数目;
  4. 第二个参数为 -1 表示检索从某一个偏移量到记录集的结束所有的记录行。

查询记录量低于100时查询时间基本没有差距,但当查询记录量越来越大所花费的时间也会越来越多。
随着查询偏移量的增大,尤其查询偏移大于10万以后查询时间急剧增加。

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

2 使用子查询优化

先定位偏移位置的 id,然后往后查询,这种方式适用于 id 递增的情况。
下面两条语句相比,第二条语句要比第一条语句快很多:

select * from orders where type = 8 limit 100000, 1;
select id from oders where type = 8 limit 100000, 1;

于是有了子查询优化如下:

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

这种方式相较原始的一般查询方法,将会增快数倍。

3 使用 id 限定优化

假设数据表的 id 时连续递增的,则查询的页数和查询的记录数可以算出查询的id范围,可以使用 id between and 来查询:

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

# 另一种写法
select * from orders where id >= 1000001 limit 100;

还可以使用 in 的方式来查询,这种方式经常用在多表关联的时候进行查询,使用其他表查询的 id 集合,来进行查询:

select * from orders where id in
(select order_id from trade where goods = 'pen')
limit 100;
4 使用临时表优化

对于使用 id 限定优化中的问题,需要 id 是连续递增的,但是在一些特定场景下,比如使用历史表的时候,或者出现过数据缺失问题时,可以考虑使用临时存储的表来记录分页的 id ,使用分页的 id 来进行 in 查询。这样能够极大的提高传统的分页查询速度,尤其是数据量上千万的时候。

题外话:关于数据表id说明

一般情况下,在数据表中建立表的时候,强制为每一张表添加 id 递增字段,这样方便查询。

如果像是订单库等数据量非常庞大,一般会进行分库分表。这时候不建议使用数据库的 id 生成器来生成,并在数据表中使用另外的字段来存储这个唯一标识。

先使用范围查询定位 id(或者索引),然后再使用索引进行定位数据,能够提高好几倍查询速度。(即先 select id 再 select *)