逻辑分页(客户端分页):从数据库将所有记录查询出来,客户端截取当前页的数据。
物理分页(数据库端分页):只从数据库中查询当前页的数据。
MyBatis默认的RowBounds采用的是逻辑分页。
private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException { if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) { if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) { rs.absolute(rowBounds.getOffset()); // skip rows by rowBounds.offset } } else { for (int i = 0; i < rowBounds.getOffset(); i++) { // skip row one by one till we hit rowBounds.offset rs.next(); } } }
但由于性能的问题,大多数系统都会采用物理分页。
(二)物理分页方式
(1)Offset Pagination
访问API时传递参数limit/offset或pageNumber/pageSize
GET /elements?limit=100&offset=100
=> the client receives the next 100 element.
OFFSET 语句在数据量大的情况下会很慢。
由于排序及数据被修改删除会产生各页结果变化。
Spring Data
/users?size=10&page=2&sort=id,desc
org.springframework.data.web.PageableHandlerMethodArgumentResolver
org.springframework.data.web.SortHandlerMethodArgumentResolver
(2)Keyset Pagination
GET /elements?pageSize=100&uuid=45678
需要额外字段
记录时间字段,date_created或date_modified
GET /elements?pageSize=100&modifiedSince=1504224000
=> the client receives the 100 elements since 1504224000. The last element of the page was modified on 1506816000.
And so on.
同一批登录的大量数据无法比较难实现(时间需要精确到毫秒才行)
(3)Continuation Tokens (aka Cursors)
Timestamp_Offset_Checksum
SELECT * FROM elementTable WHERE ( timestampColumn > T OR (timestampColumn = T AND idColumn > I) ) AND timestampColumn < now() ORDER BY timestampColumn asc, idColumn asc;
Timestamp + ID,参考:
https://blog.philipphauer.de/web-api-pagination-timestamp-id-continuation-token/