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
}

  

posted on 2025-12-04 22:00  dz45693  阅读(0)  评论(0)    收藏  举报

导航