分页接口设计

作者: gzgogo | 来源:发表于2019-03-19 10:32 被阅读1次

    两种分页方式

    传统的分页方式页最典型的特点是页面上有一连串的页码,和电梯按钮相似,因此页常被称之为电梯式分页。

    电梯式特点:

    • 通过页码进行分页
    • 通过点击上/下页按钮可实现页面切换
    • 通过点击页码可实现页面切换
    • 可直接跳转至指定页面
    • 多用于 PC 端,适合需要查找特定内容的页面
    • 需要计算总数or总页数(搜索引擎等场景也可以无需计算,相应的跳转按钮会有所限制)

    电梯式分页适用于传统的页面布局,而在移动端页面上更流行的是瀑布流布局方式,相应的分页方式称为流式分页,有时也称为无限下拉式分页。

    流式分页特点:

    • 通过滚动/上拉/点击等方式加载新一页
    • 无页码
    • 无上/下页按钮
    • 不可跳转至指定页面
    • pc端和移动端均有使用,适合UGC、视觉内容以及推荐系统等“浏览型”页面
    • 无需计算总数or总页数

    对于用电梯式分页方式的接口,有页码、页大小以及结果总数等参数,页码需要明确是从1开始还是0开始,一般使用pageNumber表示从1开始,使用pageIndex表示页索引从0开始,pageSize和limint均可表示单页数据量。totalCount和totalPage可以分别表示总数据条数和总页数。

    • page(pageNumber、pageIndex)
    • pageSize/limit
    • total(totalCount、totalPage)

    当然,对于流式分页,上述的接口设计也满足要求,但在大多数场景下,可以使用更适合的设计,比如游标式(下文会介绍)。

    常见问题

    数据缺失:获取后页时,前页数据有删除。此时,本应出现在后页的内容被“顶”到前页,而前页已经加载过了不会重新加载,后页又无此内容,从而无法被用户看到。

    数据重复:获取后页时,前页数据有插入。此时,原本在前页的内容被“压”到后页,导致前后也都有此数据,在用户端就是重复数据。

    性能问题:较大页码的数据获取时性能会下降,计算总数也会带来额外的开销。

    解决方案:

    1. 游标式分页参数设计:
    • 客户端记录当前分页的最后一条数据的 ID(curcor)
    • 请求下一页的时候,从这个 ID 开始获取一页大小(pageSize)的内容

    优点:

    • 能够避免数据重复/遗漏
    • 无需计算offset,性能更稳定

    缺点:

    • 只适用于按照时间追加的方式等简单排序
    • 无法跳到指定页,适合流式分页
    1. 一次性下发或缓存所有ID
    • 请求第 1 页数据之前/时先缓存所有 ID 列表
    • 请求第 2,3,…n 页数据时,只需传入单页相关的 ID 列表参数

    优点:

    • 可将排序由数据库移到应用容器,同时仅取ID一列,降低DB压力。
    • 无需重复计算总数,性能更优更稳定。

    缺点:

    • 仅适用于 id 列表不会很大(数百条数据)的业务场景
    1. 限定数据生成时间

    分页参数中再额外多一个timestamp参数,第一页请求时timestamp由后端生成并传给前端,前端在后页查询时将此值再次传回给后端,后端在查询条件中只用此值限定数据插入时间。

    此方法可以解决数据重复,但无法解决数据缺失,因此适用于只增不删或极少删的场景。

    常见性能问题优化方法

    1. SQL查两次,先查出所需页的ID,再用IN查询单页数据。
    2. 对热门数据缓存,如前n页。
    3. 在页数很靠后时,MySQL的limit会有比较大的性能问题,可以按倒数第n页的思路将排序方式反转查询。

    另一种分页参数设计

    在使用数据库做分页查询时,常见的方式通过行号rownum(SQL Server 、Oracle)或偏移offset(MySQL、SQL Server)来实现,因此有时候会将接口的参数设计成rowStart和rowCount。

    rowStart,起始行索引,从0开始,rowStart = pageIndex * pageSize
    rowCount,单页行数,即pageSize

    但这种行方式设计要是需要转为page参数,却容易出现不兼容,比如rowStart=1,rowCount=5,此时上述的转换关系不成立。之前在做接口切换时遇到过一次,为做到兼容,使用了如下的转换算法,本质是找出可以覆盖到rowStart到rowEnd(rowStart+rowCount)的最小pageIndex和pageSize,然后从结果中取出最终需要的subList即可。

    /// 分页参数换算
    /// </summary>
    /// <param name="rowIndexStart">起始行索引,从0开始,包含</param>
    /// <param name="rowCount">单页行数</param>
    /// <param name="pageIndex">页码索引,从0开始</param>
    /// <param name="pageSize">单页行数</param>
    /// <param name="resultIndexStart">最终结果起始索引,从0开始,包含</param>
    private static void Row2Page(int rowIndexStart, int rowCount, out int pageIndex, out int pageSize, out int resultIndexStart)
    {
      if (rowIndexStart < 0 || rowCount <= 0)
      {
        pageIndex = 0;
        pageSize = 10;
        resultIndexStart = 0;
        return;
      }
      pageSize = rowCount;
      while (rowIndexStart % pageSize + rowCount > pageSize)
      {
        pageSize++;
      }
      pageIndex = rowIndexStart / pageSize;
      resultIndexStart = rowIndexStart - pageIndex * pageSize;
    }
    

    http://zhiheng.me/156

    相关文章

      网友评论

        本文标题:分页接口设计

        本文链接:https://www.haomeiwen.com/subject/cbpbpqtx.html