go redis zset应用 缓存key关联
redis zset一般用做排名数据,比如前100名的数据,最近遇到一个BS系统,会员端需要查询一些数据总记录不多,主要是有查询过滤以及分页,原本打算把默认第一页的数据用redis缓存,后面的分页直接读db,但是遇到大厅开发不给力,希望每次都傻傻的请求,可以理解每次查询都尽力做到缓存
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
"time"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
if _, err := rdb.Ping(ctx).Result(); err != nil {
panic(err)
}
for i := 1; i < 3; i++ {
getData(ctx, rdb, i)
}
//第二此
for i := 1; i < 3; i++ {
getData(ctx, rdb, i)
}
key := "redisTest"
_ = NewCacheKeysZSet(rdb, key, 5).Clean(ctx)
fmt.Println("删除缓存")
//删除后在调用
for i := 1; i < 3; i++ {
getData(ctx, rdb, i)
}
_ = NewCacheKeysZSet(rdb, key, 5).Clean(ctx)
}
func getData(ctx context.Context, client *redis.Client, page int) (str string) {
key := "redisTest"
subKey := fmt.Sprintf("%s:page:%d", key, page)
//读缓存
str, err := client.Get(ctx, subKey).Result()
if err == nil && len(str) > 0 {
fmt.Println(fmt.Sprintf("缓存读取:%s", subKey))
return str
}
defer func() {
if len(str) > 0 {
_ = client.Set(ctx, subKey, str, time.Minute*3).Err()
_ = NewCacheKeysZSet(client, key, 5).Add(ctx, subKey, 5)
}
}()
//读db
//写缓存
str = fmt.Sprintf("this is page %d", page)
fmt.Println(fmt.Sprintf("DB读取:%s", subKey))
return str
}
getData 模拟的就是分页查询,每次都尝试读缓存,有则直接返回没有读db设置缓存,主要是这些缓存key怎么删除?这里计划用zset 来实现
package main
import (
"context"
"errors"
"github.com/redis/go-redis/v9"
"strconv"
"time"
)
type CacheKeysZSet struct {
client *redis.Client
key string
minutes int //整个key的缓存分钟数,添加的时候可以不断刷新过期时间
}
func NewCacheKeysZSet(client *redis.Client, key string, minutes int) *CacheKeysZSet {
return &CacheKeysZSet{client: client, key: key, minutes: minutes}
}
func (c *CacheKeysZSet) Add(ctx context.Context, subKey string, minutes int) error {
if len(subKey) < 1 || minutes < 1 {
return errors.New("args is error")
}
if minutes > 10 {
minutes = 10
}
currentTime := time.Now().Unix()
score := currentTime + int64(minutes)*60
/*minutes子key的过期时间当作分数,超过当前时间直接删除,*/
p := c.client.Pipeline()
p.ZAdd(ctx, c.key, redis.Z{Score: float64(score), Member: subKey})
p.Expire(ctx, c.key, time.Minute*time.Duration(c.minutes)) //刷新过期时间
p.ZRemRangeByScore(ctx, c.key, "0", strconv.FormatInt(currentTime, 10))
_, err := p.Exec(ctx)
return err
}
func (c *CacheKeysZSet) Clean(ctx context.Context) error {
currentTime := time.Now().Unix()
opt := &redis.ZRangeBy{
Min: strconv.FormatInt(currentTime, 10),
Max: "+inf",
Count: 100,
}
/*默认key最多只有100个元素,先取出存在的元素,然后删除主key,后面循环取出的元素列表 依次删除subKey*/
p := c.client.Pipeline()
p.ZRevRangeByScore(ctx, c.key, opt)
p.Del(ctx, c.key)
result, err := p.Exec(ctx)
if err != nil {
return err
}
if len(result) < 2 {
return errors.New("result length <2")
}
strCmd, ok := result[0].(*redis.StringSliceCmd)
if !ok || strCmd == nil {
return errors.New("result[0] is wrong")
}
subKeys, err := strCmd.Result()
if err != nil {
return err
}
p2 := c.client.Pipeline()
for _, k := range subKeys {
p2.Del(ctx, k)
}
_, err = p2.Exec(ctx)
return err
}
windows技术爱好者
浙公网安备 33010602011771号