基于偏移量、游标分页的详解

前言

大量的数据集往往会被分成多个空间去存储。例如一本书就会有几十页几百页,因为把一本书都放在一页去展示不管是对生产者还是消费者都是及其不友好的。又比如在网页中我们常常会看到一页一页的数据,当然我们自己开发的时候也少不了做分页展示的需求。

基于偏移量进行分页

对于分页我们相较于使用游标进行分页更熟悉、见得更多的是基于偏移量进行分页。 例如这样一个Get请求:brownjay.com/api/v1/books?page=2&size=10

这一个请求会提供一个一页的size和页数,那么我们很容易就可以知道offset为(page-1)*size

后端就会执行select *** from *** limite 10 offset 10

这种分页方式十分简单,只需跳过前面Offset指定的结果数,按需返回Limit个结果数就可以了,它很容易与数据库查询语句对应。

你有100本书,每一页展示10本,那么基于偏移量的分页方式如下。

  • 第一页:Offset:0, Limit:10
  • 第二页:Offset:10, Limit:10
  • 第三页:Offset:20, Limit:10

基于游标进行分页

基于游标分页是怎样的,我通过一个sql便知select *** from ***where id >10 limite 10

不同于使用offset进行偏移的分页,游标是通过cursor去直接进行where进行范围的筛查。 其效果都能拿到那一段区域的数据集

简单实现Demo:

我们定义一个代码分页信息的结构体。

go 复制代码
type Page struct {
	NextID        string `json:"next_id"`
	NextTimeAtUTC int64  `json:"next_time_at_utc"`
	PageSize      int64  `json:"page_size"`
}

其中:

  • NextID就是cursor
  • NextTimeAtUTC记录分页发生的时间点
  • PageSize表示每一页的元素个数

它有一个Encode方法,生成一个使用Base-64编码的令牌。

go 复制代码
// Encode 返回分页token
func (p Page) Encode() Token {
	b, err := json.Marshal(p)
	if err != nil {
		return Token("")
	}
	return Token(base64.StdEncoding.EncodeToString(b))
}

Token代表的是分页令牌,本质上是一个字符串。

go 复制代码
type Token string

它有一个Decode方法,用来从字符串令牌中解析得到分页信息。

go 复制代码
// Decode 解析分页信息
func (t Token) Decode() Page {
	var result Page
	if len(t) == 0 {
		return result
	}

	bytes, err := base64.StdEncoding.DecodeString(string(t))
	if err != nil {
		return result
	}

	err = json.Unmarshal(bytes, &result)
	if err != nil {
		return result
	}

	return result
}

这样一个简单的基于游标的分页功能就实现好了。

最终客户端会在url中携带相应token,我们就会解析出相应的nextID(cursor),而我们在vo中也会返回下一页所对应的token以便客户端进行访问

值得注意的是我们没有直接在API请求链接中使用真实数据的主键等信息,而是使用JSON序列化并使用Base-64编码的字符串来作为token来使用,这样做能防止用户直接解密我们的系统而避免风险

而为什么要在分页信息中记录时间,是为了防止token泄漏后被无限期使用 (爬虫)。我们可以限制token在一个合理时间后失效,也是安全性的一个保障。

优劣势分析

对于偏移量分页

优势:

  • 使用偏移量进行分页实现起来显然是相对更简单,并且这种实现还可以进行跳页进行查询访问

劣势:

  • 1.基于偏移量的分页在数据量很大的场景下,查询效率会比较低。通常 OFFSET 越高,查询时间就越长,而反观where进行筛选则可以使用b+树索引去进行筛选,O(logn)>>O(n)

  • 2.在并发场景下会出现元素重复(offset在第二页时有人在第一页新插入一个数据)或被跳过(offset在第二页时有人在第一页删掉了一个数据)。

  • 3.显式的page参数在支持跳页的同时也会被爬虫并发请求。


对于游标分页

优势:

  1. 1.性能好(where ***可以使用索引)
  2. 2.并发安全
  3. 3.不会被无脑批量爬取

劣势:

  • 1.实现相较于偏移量更复杂
  • 2.不支持跳页
  • 3.不适合多检索条件的场景

最后问问看到封面你会想到哪一句诗呢?😀

相关推荐
余槐i1 小时前
PostgreSQL 函数索引报错 must be immutable:3种绕法与代价
数据库·后端·sql·postgresql·性能优化
数据库小学妹1 小时前
PostgreSQL表膨胀怎么办?autovacuum清不动的根因与排查命令
数据库·postgresql·表膨胀·mvcc·数据库运维
mister_guo1 小时前
MySQL-1267错误:排序规则冲突的原因与解决方案
数据库·mysql
需要8262 小时前
Spring Bean 生命周期:从实例化到销毁的完整钩子
java·后端·spring
骇客野人2 小时前
在麒麟操作系统上 分别安装 jdk1.8, 达梦数据库,redis ,memcache 的操作实施方案和步骤
数据库·redis·memcached
凤山老林3 小时前
Spring Boot 接入国密 SM2/SM3/SM4 的一些实践记录
spring boot·后端·python
美狐美颜SDK开放平台4 小时前
直播APP如何实现美白滤镜功能?视频美颜SDK技术开发方案详解
前端·数据库·人工智能·音视频·美颜sdk·直播美颜sdk·视频美颜sdk
伯恩bourne4 小时前
Spring Boot Bean 注册的两种方式:@Component 家族 vs @Configuration + @Bean
java·spring boot·后端
张彦峰ZYF4 小时前
停顿短不等于延迟低:G1、ZGC 与低延迟服务的目标分野、代价账本与选型标定
后端·回收暂停、应用停止总时长·到达安全点、分配停滞·屏障吃吞吐、并发线程吃核心·并发窗口吃堆余量·分区式靠挑回收集兑现目标·染色指针式不支持压缩引用
pride.li4 小时前
第03章 Python 教程-基础语法:注释与变量
服务器·前端·后端·python·算法·计算机视觉