Redis实现对象存储和列表分页
场景
在项目开发中,需要用到缓存和对一个列表数据分页查询,但由于redis是key-value的存储方式,我们期望的使用类似postgresql的offset和limit,不至于需要一个个key遍历过去。
设计
分析一下我们需求,那么需求需要实现的接口大概是:
FindByPage(ctx context.Context, page, size int) ([]Object, error)
大致我们需要解决两个问题:
- 存储对象
- 列表分页快速查找
对象存储
Redis hash 是一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象。
- HDEL key field1 [field2]删除一个或多个哈希表字段
- HEXISTS key field查看哈希表 key 中,指定的字段是否存在。
- HGET key field获取存储在哈希表中指定字段的值。
- HGETALL key获取在哈希表中指定 key 的所有字段和值
如此,我们可以 redis hash 和 json.Encoding 来存储。 获得一个Object
//【ObjectID】= "{"foo": "123", "bar":"456"}"
import "github.com/go-redis/redis/v8"
result, err := rdb.HGet(ctx, "key", id.String()).Result()
if err != nil {
return
}
var obj = &YourObject{}
err = json.Unmarshal([]byte(result), obj)
if err != nil {
return
}
增加一个Object:
ob, err := json.Marshal(Object{}))
err = repo.data.rdb.HSet(ctx, "key", ob.ID, ob).Err()
if err != nil {
return err
}
分页
我们的需求中,ObjectID是一个唯一的int,那么可以使用 zset
ZXyy redis 有序集合的基本命令。
- ZADD key score1 member1 [score2 member2]向有序集合添加一个或多个成员,或者更新已存在成员的分数
- ZCARD key获取有序集合的成员数
- ZRANGE key start stop [WITHSCORES]通过索引区间返回有序集合指定区间内的成员
那么可以把 zset 的key 和 value 都设置为 ObjectID 增加一个Object的代码:
if err := rdb.ZAdd(ctx, "key", &redis.Z{
Score: float64(Object.ID),
Member: strconv.Itoa(int(Object.ID)),
}).Err(); err != nil {
return err
}
获取X分页的代码:
var (
start = int64((page - 1) * size)
end = start + int64(size)
)
result, err := rdb.ZRange(ctx, "key", start, end).Result()
if err != nil {
return
}
测试
我们增加N个Object:
[1 a]
[2 b]
[3 c]
[11 aa]
[12 bb]
[13 cc]
[21 aaaa]
每页的长度为4,获取第一页
[1 2 3 11]
第二页:
[12 13 21]
第三页:
[ ]