对于数据库相关的业务,逃不过的数据分页场景,不管是前台分页浏览仍是划到页面底部自动加载。对于分页需求,各数据库也提供了成熟的SQL支持,相似于Hibernate等ORM框架也集成了相关的方法。可是基于数据库(框架)提供的分页方法,咱们可否结合业务,提供更高效更优化的分页方法呢?数据库
数据查询语句中,和分页相关的有两个参数,分别是:api
MySQL中的语法:框架
SELECT fields_list FROM table_name [ ORDER BY ... ] [LIMIT offset, limit];
但这个语法不兼容PostgreSQL,兼容MySQL和PostgreSQL的语法为:优化
SELECT fields_list FROM table_name [ ORDER BY ... ] [ LIMIT {number | ALL} ] [ OFFSET number];
注:LIMIT和OFFSET都是可选字段。code
下文以第二种兼容的语法来进行描述。本文示例的数据库表信息以下:
示例表为订单表,表名:orders,每页显示条数:10 page_sieze
。server
查询第一页:索引
SELECT * FROM orders ORDER BY order_id LIMIT 10 OFFSET 0;
查询第二页:开发
SELECT * FROM orders ORDER BY order_id LIMIT 10 OFFSET 10;
查询第n页:get
SELECT * FROM orders ORDER BY order_id LIMIT page_sieze OFFSET page_sieze * n;
若是熟悉Openstack API的开发人员会注意到,Openstack原生API采用了特殊的分页方式:Openstack分页以limit
和marker
两个字段进行控制,limit控制每页显示数量,marker标识数据起始位置,即本分页第一条数据的ID。
以nova list的API为例,官方对两个字段的说明以下:
Nova List APIit
Limit字段说明
Requests a page size of items. Returns a number of items up to a limit value. Use the limit parameter to make an initial limited request and use the ID of the last-seen item from the response as the marker parameter value in a subsequent limited request.
Marker字段说明
The ID of the last-seen item. Use the limit parameter to make an initial limited request and use the ID of the last-seen item from the response as the marker parameter value in a subsequent limited request.
以上描述中的关键信息是:若是分页查询,返回的JSON格式中会包含marker字段,指示下一页数据第一条数据的ID。另外这个API也存在一个限制,就是分页时,只能浏览上一页/下一页,而不能跳页。
不过借鉴这个思路,咱们能够对某些场景的分页查询进行优化。
对于按需自动加载(划到页面底部自动加载更多内容)或者只提供上一页/下一页浏览模式的场景,能够进行以下优化:
查询下一页
SELECT * FROM orders WHERE order_id > page_last_id ORDER BY order_id LIMIT page_sieze OFFSET 0;
查询上一页
SELECT * FROM orders WHERE order_id < page_first_id ORDER BY order_id LIMIT page_sieze OFFSET 0;
小技巧:每次查询数据时,多返回一条数据,即返回page_size + 1条数据,但显示时去掉最后一条数据,经过这多出来一条数据,咱们能够用来判断数据是否还有下一页。
另外对于能够跳转到任意页面的场景,也能够进行优化,这种可跳转场景,分页显示也是有限的,通常模式是第一页/上一页/当前页先后10页……/下一页/最后一页,也就是说,分页时,数据是在必定范围内(先后10页)移动,能够以当前页数据为基础,对数据进行过滤,减小数据扫描范围。
考虑orders表有10W条记录,每页显示10条,当前页码为1000时的场景,若是按照单独limit和offset模式,offset=1W,也就是数据库要扫码1W条记录。假如如今翻页要从1000页跳转到1005页,咱们以第1000页最后一条数据ID为过滤条件,offset跳过1001-1004的40条数据便可。
查询1005页
SELECT * FROM orders WHERE order_id > page_1000_last_id ORDER BY order_id LIMIT page_sieze OFFSET page_size * 4;
这种方法相比基础的分页方式,只要order by字段是主键或索引字段,数据扫描的行数从1W多条降低到了几十条,效率大大提高。