go zero 结合redis实现分页列表
在实际开发中,分页列表 是一个非常常见的需求,尤其在面对大量数据时,通过分页可以有效减轻服务器和数据库的压力,提高用户体验。本篇文章将通过go zero 和 Redis 的结合,提供一个高效、灵活的分页列表实现方案,涵盖 基本分页逻辑、Redis 缓存结合 和 常见优化方法。
一、需求分析和实现方案
1.需求
在一个社交媒体平台中,每个用户可以发布多篇文章,当用户浏览文章时,需要分页加载他们的内容。考虑以下场景:
- 按 发布时间 和 点赞数 排序。
- 数据需要 支持分页,并在高并发情况下保持高性能。
- 结合 Redis 缓存 提升效率,减少数据库查询压力。
- 防止重复数据 或分页游标不一致问题。
2. 分页实现方案
分页通常分为两种实现方式:
- 基于偏移量(Offset-based Pagination): 使用 SQL 的
LIMIT
和OFFSET
实现,适合小型数据集。 - 基于游标(Cursor-based Pagination): 通过某个字段(如
id
或publish_time
)来标记分页起点,更适合大型数据集和高并发场景。
在本文中,我们主要讨论 游标分页 的实现。
完整的分页步骤总结:
- 参数校验:确保用户输入的参数有效,并设置合理的默认值。
- **排序字段设置 **:根据排序方式选择排序字段,确定游标的意义。
- **缓存查询 **:尝试从缓存中获取数据,优先使用缓存提升性能。
- **数据库查询 **:当缓存未命中时,从数据库查询数据,确保数据一致性。
- **数据排序 **:根据排序字段对数据进行排序,确保结果符合业务逻辑。
- **边界处理 **:防止分页数据重复,同时正确处理最后一页标记。
- **缓存更新 **:异步更新缓存,提升后续查询效率。
- **结果返回 **:封装分页数据、游标以及是否为最后一页的信息。
二、 项目设计
1.数据表设计
article
表:
CREATE TABLE `article` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`title` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '标题',
`content` TEXT NOT NULL COMMENT '内容',
`author_id` BIGINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '作者ID',
`like_num` INT NOT NULL DEFAULT '0' COMMENT '点赞数',
`publish_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '发布时间',
PRIMARY KEY (`id`),
INDEX `idx_author_publish_time` (`author_id`, `publish_time`)
);
2.分页接口需求
-
请求参数:
userId
:用户 ID。cursor
:上一页的最后一个游标值(如publish_time
)。pageSize
:每页的记录数量。sortType
:排序方式(0 按发布时间排序,1 按点赞数排序)。
-
返回结果:
isEnd
:是否为最后一页。cursor
:下一页的游标值。articles
:当前页的文章列表。articleId
: 最后一个文章ID
article.proto
文件:
syntax = "proto3";
package pb;
option go_package="./pb";
service Article {
rpc Articles(ArticlesRequest) returns (ArticlesResponse);
}
message ArticlesRequest {
int64 userId = 1;
int64 cursor = 2;
int64 pageSize = 3;
int64 sortType = 4;
}
message ArticleItem {
int64 Id = 1;
string title = 2;
string content = 3;
string description = 4;
string cover = 5;
int64 commentCount = 6;
int64 likeCount = 7;
int64 publishTime = 8;
int64 authorId = 9;
}
message ArticlesResponse {
repeated ArticleItem articles = 1;
bool isEnd = 2;
int64 cursor = 3;
int64 articleId = 4;
}
三、项目实现
为了进一步提高性能,可以使用 Redis 存储文章列表的分页缓存。这里使用 Redis 的有序集合(ZSET
),根据 publish_time
或 like_num
排序。
1.自定义常量
const (
SortPublishTime = iota
SortLikeCount
)
const (
articlesExpire = 3600 * 24 * 2
)
const (
DefaultPageSize = 20
DefaultLimit = 200
DefaultSortLikeCursor = 1 << 30
)
2.通过用户ID查询文章
func (m *customArticleModel) ArticlesByUserId(ctx context.Context, userId, likeNum int64, pubTime, sortField string, limit int) ([]*Article, error) {
//var anyField any
var sql string
if sortField == "like_num" {
//anyField = likeNum
//sql = fmt.Sprintf("select "+articleRows+" from "+m.table+" where user_id=? and like_num < ? order by %s desc limit ?", sortField)
sql = fmt.Sprintf("select %s from %s where `author_id`=? and like_num < %d order by %s desc limit ?", articleRows, m.table, likeNum, sortField)
} else {
//anyField = pubTime
sql = fmt.Sprintf("select %s from %s where `author_id`=? and publish_time < '%s' order by %s desc limit ?", articleRows, m.table, pubTime, sortField)
}
var articles []*Article
err := m.QueryRowsNoCacheCtx(ctx, &articles, sql, userId, limit)
if err != nil {
return