Dreamer2q Blog
见到,不如不见
Dreamer2q

Code is cheap, talk is expensive

64日志

Gorm中实现分页

创建于 2021-02-18 共 949 字,阅读约 4 分钟
浏览 10评论 0


分页,是一个很常见的现象,比如,搜索引擎给出的结果,一篇博客的目录。


为何要分页呢?


  • 为了减少传输数据

一个人在短时间获取的信息是有限的,一次搜索可能有成千上万个结果,光是这些结果就能产生很大的数据;而数据在网络中传输是需要代价的,因此分页是一种减少数据传输的手段。

  • 减少数据库压力

数据库一次性获取大量数据自然会占用许多网络资源,IO资源等。


基于Limit和Offset的分页


sql查询自带 limit 和 offset 限制,通过这个可以非常简单的实现一个分页的查询。

请求约定,分页的通用请求字段

  • Page
    • 表示第几页,由于0比较常用,因此设0表示现实意义中的第一页
  • Limit
    • 表示一页有多少数量,默认可以设置为10,请求可以设置这个参数,但是不应该超出最大值。
  • Offset
    • 表示的偏移量,SQL查询的分页就是基于这个,只不过 Offset = page*limit + offset 
    • 一般默认为0即可



这个是非常容易想到的方案,同时也是数据库就提供支持的方案。


但是我在看 gorm 的文档时,发现它推荐了一个分页库 https://github.com/raphaelvigee/go-paginate


它的方案是基于 Cursor 的,下面说一说我的理解。


基于Cursor的分页


介绍上给出的理由是

  • It scales: Unlike OFFSET/LIMIT-based pagination doesn't scale well for large datasets
  • Suitable for real-time data: having a fixed point in the flow of data prevents duplicates/missing entries


也就是说,传统基于 Offset 的做法,虽然简单,但是面临效率问题,还有就是数据集发生变化可能遗漏或者重复的问题。


而这个基于Cursor的分页,有点儿类属于 C++ 中的迭代器。C++中的迭代器,一般只能进行读取,如果数据发送了插入、删除,迭代器就可能会失效,但也有迭代器不会失效。


Cursor模式就是一个不会失效的迭代器。


要使用Cursor首先得明确一个排序,即使用哪一列作为Cursor?要升序还是降序?


一般数据表中都会有一个主键,例如 ID 这类自增的主键。


Cursor会使用你配置的列,保存一个开始位置【开始数据】,结束位置【结束数据】


然后查询数据处于(前提:已经排好序)【开始数据】和【结束数据】中的所有数据,作为一页返回。


这样就实现了分页的功能。


  • 如何进行下一页呢?


这点我其实没怎么看懂,但是我看它的代码实现了一个 Encode 和 Decode ,应该是用来编码【开始数据】和【结束数据】的,这样查询的时候,必须传入这个经过编码的数据。然后根据这个数据进行Cursor的构造,在根据 limit 查询下一页。



小结


至于还有没有其它方案,我表示不清楚,但我个人觉得基于 offset 的方案已经足够应付各种场合了。


关于 ​Cursor​ 的方案,我只是了解了一下思路,至于编码的细节不是很深入。


至于所谓的 scale 问题? 我表示太年轻,还没见过呢。