package gameKv import ( "context" "errors" "fmt" "strings" "sync" "time" "github.com/ayflying/utility_go/pkg" "github.com/ayflying/utility_go/service" "github.com/ayflying/utility_go/tools" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gconv" ) var ( Name = "game_kv" RunTimeMax *gtime.Time ) type sGameKv struct { Lock sync.Mutex } func New() *sGameKv { return &sGameKv{} } func init() { service.RegisterGameKv(New()) } // SavesV1 方法 // // @Description: 保存用户KV数据列表。 // @receiver s: sGameKv的实例。 // @return err: 错误信息,如果操作成功,则为nil。 func (s *sGameKv) SavesV1() (err error) { var ctx = gctx.New() RunTimeMax = gtime.Now().Add(time.Minute * 30) g.Log().Debug(ctx, "开始执行游戏kv数据保存") err = tools.Redis.RedisScanV2("user:kv:*", func(keys []string) (err error) { if gtime.Now().After(RunTimeMax) { g.Log().Error(ctx, "kv执行超时了,停止执行!") err = errors.New("kv执行超时了,停止执行!") return } if err = s.SavesV2Batch(ctx, keys); err != nil { g.Log().Errorf(ctx, "批量保存KV失败: %v", err) } return }) return } // 删除缓存key func (s *sGameKv) DelCacheKey(ctx context.Context, uid int64) { //如果有活跃,跳过删除 if getBool, _ := pkg.Cache("redis"). Contains(ctx, fmt.Sprintf("act:update:%d", uid)); getBool { return } cacheKey := fmt.Sprintf("user:kv:%v", uid) _, err := g.Redis().Del(ctx, cacheKey) if err != nil { g.Log().Error(ctx, err) } } // SavesV2Batch 批量保存游戏KV数据 (优化版) // // @Description: 使用批量Redis MGET和批量数据库操作提升性能 // @param ctx context.Context: 上下文对象 // @param cacheKeys []string: 缓存键列表 // @return err error: 返回错误信息 func (s *sGameKv) SavesV2Batch(ctx context.Context, cacheKeys []string) (err error) { if len(cacheKeys) == 0 { return nil } type keyInfo struct { cacheKey string uid int64 } var keyInfos []keyInfo for _, cacheKey := range cacheKeys { result := strings.Split(cacheKey, ":") if len(result) < 3 { continue } uid := gconv.Int64(result[2]) if uid == 0 { continue } if getBool, _ := pkg.Cache("redis").Contains(ctx, fmt.Sprintf("act:update:%d", uid)); getBool { continue } keyInfos = append(keyInfos, keyInfo{ cacheKey: cacheKey, uid: uid, }) } if len(keyInfos) == 0 { return nil } redisValues, err := g.Redis().MGet(ctx, cacheKeys...) if err != nil { g.Log().Errorf(ctx, "批量获取Redis失败: %v", err) return err } type ListData struct { Uid int64 `json:"uid"` Kv interface{} `json:"kv"` } var list []*ListData for _, ki := range keyInfos { val, ok := redisValues[ki.cacheKey] if !ok { continue } var data interface{} if val != nil { gconv.Scan(val, &data) } if data == nil { continue } list = append(list, &ListData{ Uid: ki.uid, Kv: data, }) } if len(list) == 0 { return nil } _, err = g.Model(Name).Data(list).Save() if err != nil { g.Log().Errorf(ctx, "批量保存KV失败: %v", err) return err } for _, v := range list { s.DelCacheKey(ctx, v.Uid) } g.Log().Debugf(ctx, "SavesV2Batch完成: 保存%d条", len(list)) return nil }