一、连接管理
1. 基础连接
go
import"github.com/redis/go-redis/v9"// 单机连接rdb:=redis.NewClient(&redis.Options{Addr:"localhost:6379",Password:"",// 无密码DB:0,// 默认DB})// 集群连接rdb:=redis.NewClusterClient(&redis.ClusterOptions{Addrs:[]string{":7000",":7001",":7002"},})2. 连接池配置
go
rdb:=redis.NewClient(&redis.Options{PoolSize:100,// 连接池大小MinIdleConns:10,// 最小空闲连接MaxRetries:3,// 最大重试次数DialTimeout:5*time.Second,ReadTimeout:3*time.Second,WriteTimeout:3*time.Second,PoolTimeout:4*time.Second,})二、String 字符串
基本操作
go
ctx := context.Background() // SET/GET rdb.Set(ctx, "key", "value", 0) val, err := rdb.Get(ctx, "key").Result() // 带过期时间 rdb.SetEx(ctx, "key", "value", 10*time.Second) // 批量操作 rdb.MSet(ctx, "key1", "value1", "key2", "value2") rdb.MGet(ctx, "key1", "key2") // 自增/自减 rdb.Incr(ctx, "counter") rdb.IncrBy(ctx, "counter", 5)三、Hash 哈希表
go
// HSET/HGETrdb.HSet(ctx,"user:1000","name","John","age",30)name:=rdb.HGet(ctx,"user:1000","name").Val()// 获取所有字段all:=rdb.HGetAll(ctx,"user:1000").Val()// HMSET/HMGETrdb.HMSet(ctx,"user:1001",map[string]interface{}{"name":"Alice","age":25,})// 字段自增rdb.HIncrBy(ctx,"user:1000","age",1)// 删除字段rdb.HDel(ctx,"user:1000","age")四、List 列表
go
// LPUSH/RPUSHrdb.LPush(ctx,"mylist","value1","value2")rdb.RPush(ctx,"mylist","value3")// LPOP/RPOPval:=rdb.LPop(ctx,"mylist").Val()// LRANGEitems:=rdb.LRange(ctx,"mylist",0,-1).Val()// LTRIM 保留指定范围rdb.LTrim(ctx,"mylist",0,9)// 阻塞操作result:=rdb.BLPop(ctx,5*time.Second,"mylist")五、Set 集合
go
// SADD/SMEMBERSrdb.SAdd(ctx,"myset","member1","member2")members:=rdb.SMembers(ctx,"myset").Val()// SISMEMBERexists:=rdb.SIsMember(ctx,"myset","member1").Val()// 集合运算rdb.SUnion(ctx,"set1","set2")// 并集rdb.SInter(ctx,"set1","set2")// 交集rdb.SDiff(ctx,"set1","set2")// 差集// SCARD 获取集合大小size:=rdb.SCard(ctx,"myset").Val()六、Sorted Set 有序集合
go
// ZADDrdb.ZAdd(ctx,"leaderboard",redis.Z{Score:100,Member:"player1",})// 批量添加members:=[]redis.Z{{Score:200,Member:"player2"},{Score:150,Member:"player3"},}rdb.ZAdd(ctx,"leaderboard",members...)// ZRANGE 按分数排序rdb.ZRange(ctx,"leaderboard",0,-1)// 升序rdb.ZRevRange(ctx,"leaderboard",0,-1)// 降序// 带分数返回results:=rdb.ZRangeWithScores(ctx,"leaderboard",0,-1).Val()// ZRANK 获取排名rank:=rdb.ZRank(ctx,"leaderboard","player1").Val()// ZSCORE 获取分数score:=rdb.ZScore(ctx,"leaderboard","player1").Val()// ZINCRBY 增加分数rdb.ZIncrBy(ctx,"leaderboard",50,"player1")// ZCOUNT 分数区间计数count:=rdb.ZCount(ctx,"leaderboard","100","200").Val()七、Stream 流
go
// XADD 添加消息id,err:=rdb.XAdd(ctx,&redis.XAddArgs{Stream:"mystream",ID:"*",// 自动生成IDValues:map[string]interface{}{"field1":"value1","field2":"value2",},}).Result()// XREAD 读取消息messages,err:=rdb.XRead(ctx,&redis.XReadArgs{Streams:[]string{"mystream","0"},Count:10,Block:0,}).Result()// 消费者组// 创建消费者组rdb.XGroupCreate(ctx,"mystream","mygroup","0")// 消费者读取messages,err:=rdb.XReadGroup(ctx,&redis.XReadGroupArgs{Group:"mygroup",Consumer:"consumer1",Streams:[]string{"mystream",">"},Count:10,Block:0,}).Result()// 确认消息rdb.XAck(ctx,"mystream","mygroup",id)八、事务
go
// MULTI/EXECpipe:=rdb.TxPipeline()pipe.Set(ctx,"key1","value1",0)pipe.Incr(ctx,"counter")pipe.Expire(ctx,"key1",time.Hour)_,err:=pipe.Exec(ctx)// Watch 乐观锁err:=rdb.Watch(ctx,func(tx*redis.Tx)error{// 获取当前值val,err:=tx.Get(ctx,"key").Int()iferr!=nil&&err!=redis.Nil{returnerr}// 事务开始_,err=tx.TxPipelined(ctx,func(pipe redis.Pipeliner)error{pipe.Set(ctx,"key",val+1,0)returnnil})returnerr},"key")九、发布订阅
go
// 创建订阅者pubsub:=rdb.Subscribe(ctx,"mychannel")// 接收消息ch:=pubsub.Channel()formsg:=rangech{fmt.Println(msg.Channel,msg.Payload)}// 发布消息err:=rdb.Publish(ctx,"mychannel","hello").Err()// 模式订阅pubsub:=rdb.PSubscribe(ctx,"news.*")// 关闭订阅pubsub.Close()十、管道(Pipeline)
go
pipe:=rdb.Pipeline()pipe.Set(ctx,"key1","value1",0)pipe.Get(ctx,"key1")pipe.Expire(ctx,"key1",time.Hour)cmds,err:=pipe.Exec(ctx)十一、实用技巧
1. 错误处理
go
val,err:=rdb.Get(ctx,"key").Result()iferr==redis.Nil{fmt.Println("key does not exist")}elseiferr!=nil{panic(err)}2. Scan 命令处理大数据集
go
varcursoruint64for{varkeys[]stringkeys,cursor,err=rdb.Scan(ctx,cursor,"pattern:*",100).Result()// 处理keysifcursor==0{break}}3. 连接健康检查
err:=rdb.Ping(ctx).Err()iferr!=nil{// 处理连接错误}十二、案例讲解
packagemainimport("context""fmt""log""math/rand""time""github.com/redis/go-redis/v9")// ============================ 数据结构定义 ============================// GameRankingSystem 游戏排行榜系统结构体// 封装了Redis客户端和上下文,提供统一的游戏服务接口typeGameRankingSystemstruct{rdb*redis.Client// Redis客户端实例,用于所有Redis操作ctx context.Context// 上下文,用于控制请求超时和取消}// User 用户结构体// 表示游戏中的用户,包含用户的基本信息和资产typeUserstruct{IDstring// 用户唯一标识Usernamestring// 用户名Levelint// 用户等级Coinsint// 用户金币数量}// ============================ 系统初始化 ============================// NewGameRankingSystem 创建排行榜系统实例// 返回值: *GameRankingSystem 初始化好的游戏系统实例funcNewGameRankingSystem()*GameRankingSystem{// 创建 Redis 客户端配置// Options结构体包含了连接Redis所需的所有配置参数rdb:=redis.NewClient(&redis.Options{Addr:"localhost:6379",// Redis服务器地址Password:"",// 密码,空字符串表示无密码DB:0,// 数据库编号,0表示默认数据库PoolSize:20,// 连接池大小,控制最大并发连接数})// 创建上下文,用于控制请求的生命周期ctx:=context.Background()// 测试连接是否成功// Ping命令是Redis最简单的命令,用于测试连通性iferr:=rdb.Ping(ctx).Err();err!=nil{// 连接失败时,终止程序运行log.Fatalf("Redis连接失败: %v",err)}// 返回初始化好的系统实例return&GameRankingSystem{rdb:rdb,ctx:ctx,}}// ============================ String 操作示例 ============================// StringOperations 演示String类型的基本操作// String是最简单的Redis数据类型,常用于存储简单的键值对func(g*GameRankingSystem)StringOperations(){fmt.Println("\n=== String 操作演示 ===")// 1. 存储用户会话信息(String)// 场景:用户登录后创建会话,30分钟后自动过期// 键名模式:session:{userID},便于管理和查找sessionKey:=fmt.Sprintf("session:%s","user123")// SetEx命令:设置键值对并指定过期时间// 参数说明:ctx上下文,key键名,value值,expiration过期时间g.rdb.SetEx(g.ctx,sessionKey,"logged_in",30*time.Minute)fmt.Println("✅ 用户会话已设置,30分钟后过期")// 2. 每日登录奖励计数器// 场景:统计每天有多少用户登录,用于活动分析// 键名模式:daily:login:{date},按日期组织数据today:=time.Now().Format("2006-01-02")dailyLoginKey:=fmt.Sprintf("daily:login:%s",today)// Incr命令:将键存储的数字值增加1// 如果键不存在,则会先创建并设置为0,再执行+1操作count,_:=g.rdb.Incr(g.ctx,dailyLoginKey).Result()// Expire命令:设置键的过期时间// 这里设置48小时过期,保留2天的统计数据g.rdb.Expire(g.ctx,dailyLoginKey,48*time.Hour)fmt.Printf("✅ 今日第 %d 位用户登录\n",count)// 3. 获取会话状态// 模拟用户访问时的会话验证status,err:=g.rdb.Get(g.ctx,sessionKey).Result()iferr==redis.Nil{// redis.Nil是特殊的错误类型,表示键不存在fmt.Println("❌ 会话不存在或已过期")}elseiferr!=nil{// 其他错误类型,如网络错误、Redis服务错误等fmt.Printf("❌ 获取会话失败: %v\n",err)}else{// 获取成功,输出会话状态fmt.Printf("✅ 用户会话状态: %s\n",status)}}// ============================ Hash 操作示例 ============================// HashOperations 演示Hash类型的基本操作// Hash是field-value结构,适合存储对象func(g*GameRankingSystem)HashOperations(user User){fmt.Println("\n=== Hash 操作演示 ===")// 用户信息键名// 键名模式:user:{userID},使用Hash存储用户的所有属性userKey:=fmt.Sprintf("user:%s",user.ID)// 1. 存储用户信息到 Hash(一个对象多个字段)// HSet命令:设置Hash中的字段值// 可以一次性设置多个字段,减少网络请求次数g.rdb.HSet(g.ctx,userKey,"username",user.Username,// 字符串字段"level",user.Level,// 数字字段"coins",user.Coins,// 数字字段"last_login",time.Now().Format(time.RFC3339),// 时间字段)fmt.Println("✅ 用户信息已存储到 Hash")// 2. 获取用户特定字段// HGet命令:获取Hash中指定字段的值username,_:=g.rdb.HGet(g.ctx,userKey,"username").Result()coins,_:=g.rdb.HGet(g.ctx,userKey,"coins").Int()fmt.Printf("✅ 用户名: %s, 金币数: %d\n",username,coins)// 3. 增加用户金币(字段自增)// HIncrBy命令:将Hash中指定字段的值增加给定数值// 原子操作,适用于计数器和余额增减场景newCoins,_:=g.rdb.HIncrBy(g.ctx,userKey,"coins",100).Result()fmt.Printf("✅ 获得100金币奖励,当前金币: %d\n",newCoins)// 4. 获取所有用户信息// HGetAll命令:获取Hash中所有字段和值// 返回map[string]string类型,适用于获取完整对象allInfo,_:=g.rdb.HGetAll(g.ctx,userKey).Result()fmt.Printf("✅ 用户完整信息: %v\n",allInfo)// 5. 检查字段是否存在// HExists命令:检查Hash中指定字段是否存在exists,_:=g.rdb.HExists(g.ctx,userKey,"level").Result()fmt.Printf("✅ level字段是否存在: %v\n",exists)}// ============================ Sorted Set 操作示例 ============================// SortedSetOperations 演示Sorted Set类型的基本操作// Sorted Set是有序集合,每个元素都有一个分数用于排序func(g*GameRankingSystem)SortedSetOperations(users[]User){fmt.Println("\n=== Sorted Set 操作演示 ===")// 排行榜键名leaderboardKey:="leaderboard:global"// 1. 批量添加用户到排行榜(ZADD)// 准备要添加到Sorted Set的成员列表varmembers[]redis.Z// redis.Z是Sorted Set成员的结构体for_,user:=rangeusers{// 计算用户分数// 分数设计:level * 1000 + coins// 这样高等级玩家总是排在低等级玩家前面,同等级玩家按金币数排序score:=float64(user.Level*1000+user.Coins)members=append(members,redis.Z{Score:score,// 排序分数Member:user.Username,// 成员标识})}// ZAdd命令:向Sorted Set添加成员// 如果成员已存在,则更新其分数g.rdb.ZAdd(g.ctx,leaderboardKey,members...)fmt.Println("✅ 用户排行榜已更新")// 2. 获取前3名玩家(ZREVRANGE)// 降序排列,分数高的在前面fmt.Println("\n🏆 排行榜前三名:")top3,_:=g.rdb.ZRevRangeWithScores(g.ctx,leaderboardKey,0,2).Result()fori,player:=rangetop3{fmt.Printf("第%d名: %s (分数: %.0f)\n",i+1,player.Member,player.Score)}// 3. 获取用户排名(ZREVRANK)// 获取成员在Sorted Set中的排名(从0开始)for_,user:=rangeusers{rank,err:=g.rdb.ZRevRank(g.ctx,leaderboardKey,user.Username).Result()iferr==redis.Nil{// 用户不在排行榜中fmt.Printf("❌ 用户 %s 不在排行榜中\n",user.Username)}else{// 排名从0开始,所以需要+1显示人类可读的排名fmt.Printf("👤 用户 %s 的排名: 第%d名\n",user.Username,rank+1)}}// 4. 获取分数段玩家数量(ZCOUNT)// 统计指定分数范围内的成员数量minScore:="5000"maxScore:="8000"count,_:=g.rdb.ZCount(g.ctx,leaderboardKey,minScore,maxScore).Result()fmt.Printf("📊 分数在 %s-%s 之间的玩家数: %d\n",minScore,maxScore,count)}// ============================ Set 操作示例 ============================// SetOperations 演示Set类型的基本操作// Set是无序集合,元素不重复,支持集合运算func(g*GameRankingSystem)SetOperations(user User){fmt.Println("\n=== Set 操作演示 ===")// 1. 用户好友集合// 键名模式:user:{userID}:friendsfriendsKey:=fmt.Sprintf("user:%s:friends",user.ID)// SAdd命令:向Set添加成员// 如果成员已存在,则不会重复添加g.rdb.SAdd(g.ctx,friendsKey,"user456","user789","user999")fmt.Println("✅ 好友列表已添加")// 2. 获取所有好友// SMembers命令:获取Set中所有成员// 注意:Set是无序的,每次返回的顺序可能不同friends,_:=g.rdb.SMembers(g.ctx,friendsKey).Result()fmt.Printf("👥 %s 的好友列表: %v\n",user.Username,friends)// 3. 检查是否是好友// SIsMember命令:检查指定成员是否在Set中isFriend,_:=g.rdb.SIsMember(g.ctx,friendsKey,"user456").Result()fmt.Printf("❓ user456 是否是好友: %v\n",isFriend)// 4. 随机推荐好友(SRANDMEMBER)// SRandMember命令:从Set中随机返回一个成员// 可用于好友推荐、抽奖等场景randomFriend,_:=g.rdb.SRandMember(g.ctx,friendsKey).Result()fmt.Printf("🎲 随机推荐好友: %s\n",randomFriend)// 5. 好友数量// SCard命令:获取Set中成员的数量friendCount,_:=g.rdb.SCard(g.ctx,friendsKey).Result()fmt.Printf("📊 好友总数: %d\n",friendCount)}// ============================ List 操作示例 ============================// ListOperations 演示List类型的基本操作// List是有序列表,元素可以重复,支持从两端操作func(g*GameRankingSystem)ListOperations(user User){fmt.Println("\n=== List 操作演示 ===")// 1. 用户最近游戏记录(List作为队列)// 键名模式:user:{userID}:game_history// 使用List存储用户最近的游戏记录gameHistoryKey:=fmt.Sprintf("user:%s:game_history",user.ID)// 添加最近的游戏记录// LPush命令:从列表左侧插入元素,最新记录在最前面fori:=1;i<=5;i++{gameInfo:=fmt.Sprintf("游戏%d_得分%d_时间%s",i,rand.Intn(1000),// 随机生成得分time.Now().Format("15:04:05"))// 当前时间g.rdb.LPush(g.ctx,gameHistoryKey,gameInfo)}fmt.Println("✅ 游戏记录已保存")// 2. 获取最近3场游戏(LRANGE)// LRange命令:获取列表中指定范围的元素// 参数:0表示第一个元素,2表示第三个元素(包含)fmt.Println("\n🎮 最近3场游戏记录:")recentGames,_:=g.rdb.LRange(g.ctx,gameHistoryKey,0,2).Result()fori,game:=rangerecentGames{fmt.Printf(" %d. %s\n",i+1,game)}// 3. 保持最近10条记录(LTRIM)// LTrim命令:修剪列表,只保留指定范围内的元素// 参数:0表示起始索引,9表示结束索引// 常用于实现固定大小的历史记录列表g.rdb.LTrim(g.ctx,gameHistoryKey,0,9)fmt.Println("✅ 已修剪,只保留最近10条记录")// 4. 获取列表长度// LLen命令:获取列表的长度historyLength,_:=g.rdb.LLen(g.ctx,gameHistoryKey).Result()fmt.Printf("📊 游戏历史记录总数: %d\n",historyLength)}// ============================ 事务操作示例 ============================// TransactionOperations 演示事务操作// Redis事务可以保证多个命令的原子性执行func(g*GameRankingSystem)TransactionOperations(user1,user2 User){fmt.Println("\n=== 事务操作演示 ===")// 场景:用户之间转账金币,需要保证原子性// 必须确保扣款和入账同时成功或同时失败// 用户信息键名user1Key:=fmt.Sprintf("user:%s",user1.ID)user2Key:=fmt.Sprintf("user:%s",user2.ID)transferAmount:=50// 转账金额// 使用事务管道(TxPipeline)fmt.Printf("💰 开始转账: %s 给 %s 转账 %d 金币\n",user1.Username,user2.Username,transferAmount)// 开始事务// Watch命令:监控指定的键,如果在事务执行期间被其他客户端修改,则事务失败// 这是Redis的乐观锁机制err:=g.rdb.Watch(g.ctx,func(tx*redis.Tx)error{// 在Watch回调中,所有操作都必须通过tx对象执行// 获取双方当前金币数量coins1,err:=tx.HGet(g.ctx,user1Key,"coins").Int()iferr!=nil&&err!=redis.Nil{returnerr}coins2,err:=tx.HGet(g.ctx,user2Key,"coins").Int()iferr!=nil&&err!=redis.Nil{returnerr}fmt.Printf("转账前: %s=%d金币, %s=%d金币\n",user1.Username,coins1,user2.Username,coins2)// 检查余额是否足够// 业务逻辑检查必须在事务中执行ifcoins1<transferAmount{returnfmt.Errorf("余额不足")}// 执行事务// TxPipelined命令:在事务中执行多个命令_,err=tx.TxPipelined(g.ctx,func(pipe redis.Pipeliner)error{// 扣减转出方金币// HIncrBy命令的负数参数表示减少pipe.HIncrBy(g.ctx,user1Key,"coins",int64(-transferAmount))// 增加转入方金币pipe.HIncrBy(g.ctx,user2Key,"coins",int64(transferAmount))// 记录转账日志(可选,用于审计)transferLog:=fmt.Sprintf("transfer:%s:%s:%d",user1.ID,user2.ID,time.Now().Unix())pipe.Set(g.ctx,transferLog,transferAmount,24*time.Hour)returnnil})returnerr},user1Key,user2Key)// Watch 监视的键,这些键在事务期间不能改变// 处理事务结果iferr!=nil{fmt.Printf("❌ 转账失败: %v\n",err)}else{fmt.Println("✅ 转账成功!")// 验证转账结果newCoins1,_:=g.rdb.HGet(g.ctx,user1Key,"coins").Int()newCoins2,_:=g.rdb.HGet(g.ctx,user2Key,"coins").Int()fmt.Printf("转账后: %s=%d金币, %s=%d金币\n",user1.Username,newCoins1,user2.Username,newCoins2)}}// ============================ 发布订阅示例 ============================// PubSubOperations 演示发布订阅模式// Redis Pub/Sub 是一种消息通信模式,发送者发送消息,订阅者接收消息func(g*GameRankingSystem)PubSubOperations(){fmt.Println("\n=== 发布订阅演示 ===")channelName:="game:announcements"// 频道名称// 启动一个goroutine作为订阅者// 在实际应用中,订阅者通常是独立的服务或进程gofunc(){// Subscribe命令:订阅指定频道pubsub:=g.rdb.Subscribe(g.ctx,channelName)// defer确保在函数退出时关闭订阅,释放资源deferpubsub.Close()// Channel方法:返回一个Go channel,用于接收消息ch:=pubsub.Channel()fmt.Println("👂 订阅者已启动,等待消息...")// 循环接收消息formsg:=rangech{// msg包含频道名称和消息内容fmt.Printf("📢 收到公告: %s\n",msg.Payload)// 收到特定消息后退出// 这是演示用的控制逻辑,实际应用中可能不需要ifmsg.Payload=="exit"{fmt.Println("⏹️ 订阅者退出")return}}}()// 给订阅者一点时间启动,确保订阅者已经连接// 在实际生产环境中,应该有更好的同步机制time.Sleep(100*time.Millisecond)// 发布消息fmt.Println("\n📡 发布游戏公告...")// 模拟要发布的消息列表announcements:=[]string{"系统维护通知:今晚23:00-24:00进行维护","新活动上线:周末双倍经验活动开始!","恭喜玩家 '游戏高手' 获得全服首杀!","exit",// 特殊消息,让订阅者退出}// 依次发布每条消息for_,msg:=rangeannouncements{// Publish命令:向指定频道发布消息// 所有订阅了该频道的客户端都会收到消息g.rdb.Publish(g.ctx,channelName,msg)// 模拟延迟,让输出更清晰time.Sleep(500*time.Millisecond)}}// ============================ 批量操作示例 ============================// PipelineOperations 演示Pipeline批量操作// Pipeline可以将多个命令一次性发送给Redis,减少网络往返时间func(g*GameRankingSystem)PipelineOperations(users[]User){fmt.Println("\n=== Pipeline 批量操作演示 ===")// 使用 Pipeline 批量执行命令,减少网络往返// Pipeline不是事务,不保证原子性,但能提高性能pipe:=g.rdb.Pipeline()// 批量准备命令for_,user:=rangeusers{userKey:=fmt.Sprintf("user:%s:stats",user.ID)// 批量设置命令// 这些命令会被缓存,不会立即发送到Redispipe.HSet(g.ctx,userKey,"kills",rand.Intn(100),// 随机生成击杀数"deaths",rand.Intn(50),// 随机生成死亡数"assists",rand.Intn(30),// 随机生成助攻数)// 设置过期时间pipe.Expire(g.ctx,userKey,time.Hour)}// 执行所有命令fmt.Println("🚀 正在批量执行命令...")start:=time.Now()// Exec命令:一次性发送所有缓存的命令到Rediscmds,err:=pipe.Exec(g.ctx)elapsed:=time.Since(start)iferr!=nil{fmt.Printf("❌ Pipeline执行失败: %v\n",err)return}// 输出执行结果fmt.Printf("✅ 批量执行完成,处理了 %d 个用户\n",len(users))fmt.Printf("⏱️ 总耗时: %v (使用普通命令需 %v 左右)\n",elapsed,time.Duration(len(users)*2)*time.Millisecond)// cmds包含每个命令的执行结果,如果需要可以进一步处理_=cmds// 这里不需要使用,所以用下划线忽略}// ============================ 清理数据 ============================// Cleanup 清理测试数据// 在生产环境中应该小心使用,这里是为了演示后清理测试数据func(g*GameRankingSystem)Cleanup(patterns...string){fmt.Println("\n=== 清理测试数据 ===")// 遍历所有要清理的模式for_,pattern:=rangepatterns{// 使用 SCAN 迭代删除,避免阻塞// SCAN命令比KEYS命令更安全,不会阻塞Redis服务varcursoruint64for{varfoundKeys[]stringvarerrerror// SCAN命令:迭代遍历匹配模式的键// 参数说明:cursor游标,pattern匹配模式,count每次返回的键数量foundKeys,cursor,err=g.rdb.Scan(g.ctx,cursor,pattern,10).Result()iferr!=nil{break}// 如果找到匹配的键,删除它们iflen(foundKeys)>0{// Del命令:删除一个或多个键g.rdb.Del(g.ctx,foundKeys...)fmt.Printf("🧹 已删除: %v\n",foundKeys)}// cursor为0表示迭代结束ifcursor==0{break}}}fmt.Println("✅ 数据清理完成")}// ============================ 主函数 ============================// main 程序入口函数funcmain(){fmt.Println("🎮 Redis 游戏排行榜系统演示")fmt.Println("="*50)// 初始化系统// 创建GameRankingSystem实例,建立Redis连接gameSystem:=NewGameRankingSystem()// defer确保程序退出时关闭Redis连接,释放资源deferfunc(){iferr:=gameSystem.rdb.Close();err!=nil{fmt.Printf("关闭Redis连接时出错: %v\n",err)}}()// 创建测试用户数据// 这些用户用于演示各种Redis操作users:=[]User{{ID:"1001",Username:"游戏高手",Level:10,Coins:5000},{ID:"1002",Username:"幸运玩家",Level:8,Coins:3000},{ID:"1003",Username:"新手玩家",Level:3,Coins:1000},{ID:"1004",Username:"氪金大佬",Level:15,Coins:10000},}// 初始化随机数种子// 确保每次运行生成的随机数不同rand.Seed(time.Now().UnixNano())// 执行各个功能演示// 按顺序演示Redis的各种数据结构和功能// 1. String操作演示gameSystem.StringOperations()// 2. Hash操作演示(使用第一个用户)gameSystem.HashOperations(users[0])// 3. Sorted Set操作演示(排行榜功能)gameSystem.SortedSetOperations(users)// 4. Set操作演示(好友功能)gameSystem.SetOperations(users[0])// 5. List操作演示(游戏历史记录)gameSystem.ListOperations(users[0])// 6. 事务操作演示(转账功能)gameSystem.TransactionOperations(users[0],users[1])// 7. Pipeline批量操作演示gameSystem.PipelineOperations(users)// 8. 发布订阅演示(需要异步执行)gameSystem.PubSubOperations()// 等待发布订阅演示完成// 因为发布订阅是异步的,需要等待足够的时间让消息被处理time.Sleep(3*time.Second)// 清理测试数据// 删除演示过程中创建的所有测试数据,避免影响下次运行gameSystem.Cleanup("user:*",// 所有用户数据"session:*",// 会话数据"daily:*",// 每日统计数据"leaderboard:*",// 排行榜数据"transfer:*",// 转账日志数据)fmt.Println("\n🎉 演示完成!")fmt.Println("\n📚 总结:")fmt.Println("1. String: 适合简单键值对、计数器和缓存")fmt.Println("2. Hash: 适合存储对象和结构化数据")fmt.Println("3. Sorted Set: 适合排行榜和范围查询")fmt.Println("4. Set: 适合集合运算和不重复列表")fmt.Println("5. List: 适合队列和历史记录")fmt.Println("6. 事务: 适合需要原子性的操作")fmt.Println("7. Pipeline: 适合批量操作,提高性能")fmt.Println("8. Pub/Sub: 适合实时消息通知")}核心知识点总结:
连接管理最佳实践
连接池配置:PoolSize 控制最大并发连接数,避免连接过多连接测试:使用 Ping() 验证连接有效性
资源释放:使用 defer 确保连接关闭,防止资源泄漏
数据结构选择原则
String:简单缓存、计数器、会话管理Hash:用户信息、商品信息等结构化数据
Sorted Set:排行榜、带权重的队列
Set:好友关系、标签系统、共同关注
List:消息队列、最新动态、操作日志
事务 vs Pipeline
事务(Transaction):保证原子性,适合转账、库存扣减流水线(Pipeline):提高性能,适合批量操作但不需原子性
性能优化技巧
使用Pipeline:减少网络往返,批量操作提升性能合理设置过期时间:避免内存泄漏,自动清理过期数据
使用SCAN替代KEYS:避免阻塞Redis服务
连接池配置:根据并发量调整连接池大小
错误处理要点
区分redis.Nil:键不存在的特殊错误,需单独处理事务失败处理:Watch失败时重试或回退
连接错误:实现重连机制和降级策略
实际应用场景
游戏系统:排行榜(Sorted Set)、用户信息(Hash)、好友(Set)电商系统:购物车(Hash)、商品库存(String)、订单队列(List)
社交系统:关注关系(Set)、动态流(List)、消息通知(Pub/Sub)