Code is cheap, talk is expensive
分页,是一个很常见的现象,比如,搜索引擎给出的结果,一篇博客的目录。
一个人在短时间获取的信息是有限的,一次搜索可能有成千上万个结果,光是这些结果就能产生很大的数据;而数据在网络中传输是需要代价的,因此分页是一种减少数据传输的手段。
数据库一次性获取大量数据自然会占用许多网络资源,IO资源等。
sql查询自带 limit
和 offset
限制,通过这个可以非常简单的实现一个分页的查询。
请求约定,分页的通用请求字段
Offset
= page*limit
+ offset
这个是非常容易想到的方案,同时也是数据库就提供支持的方案。
但是我在看 gorm
的文档时,发现它推荐了一个分页库 https://github.com/raphaelvigee/go-paginate
它的方案是基于 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
问题? 我表示太年轻,还没见过呢。