我不会起名字322· 后端 / 算法 / 数据库 技术栈 力扣 Hot100 Go 项目 Redis MySQL文章目录Redis 从 0 到 1三五大结构 20 个最常用命令用 Go 各写一个真实用法附速查表一、先纠正一个学习顺序二、Go 实操5 个场景 20 个命令场景 1String —— 缓存用户对象 接口访问计数场景 2Hash —— 购物车字段级增删改场景 3List —— 最新消息时间线场景 4Set —— 共同关注与标签场景 5ZSet —— 排行榜第 15 篇有完整的工程细节三、20 命令速查表收藏点 1四、容易踩的 3 个坑五、总结Redis 从 0 到 1三五大结构 20 个最常用命令用 Go 各写一个真实用法附速查表上一篇《Redis 从 0 到 1二Go 连 Redis 半夜总报 EOF连接池 5 参数与 redisx 封装》我们做出了全系列复用的 redisx 连接封装链接https://blog.csdn.net/2501_92769340/article/details/167399361 。本篇开始用连接干活五大结构的 20 个最常用命令每个都放进真实业务场景里用 Go 写一遍。一、先纠正一个学习顺序很多人学 Redis 命令的方式是把 String 的 30 个命令背一遍三个月后只记得 SET 和 GET。问题不在记忆力在于命令没有挂在场景上。面试官问购物车用什么结构能答上来的人才是真的会了。本篇的学习路径反着来先给 5 个真实场景再看每个场景为什么选这个结构、用哪几个命令。场景结构为什么选它缓存用户对象 / 计数器String最简单序列化整存整取INCR 原子计数商品详情 / 购物车Hash字段级读写改一个字段不用整取整个对象最新消息列表 / 简单队列List有序、两头进出LPUSHLRANGE 就是时间线共同关注 / 标签去重Set去重 交集并集是原生操作排行榜 / 延迟任务ZSet每个元素带分数按分数排序 O(logN)二、Go 实操5 个场景 20 个命令以下代码基于上一篇的 redisx 封装省略 main 函数框架每段都是可直接运行的核心逻辑完整代码见系列仓库。场景 1String —— 缓存用户对象 接口访问计数funcdemoString(ctx context.Context,rdb*redis.Client)error{// 1. SETEX缓存对象必须带 TTL第 11 篇讲为什么userJSON:{id:1001,name:zhangsan}iferr:rdb.SetEx(ctx,user:1001,userJSON,30*time.Minute).Err();err!nil{returnfmt.Errorf(缓存用户失败: %w,err)}// 2. GET 反序列化val,err:rdb.Get(ctx,user:1001).Result()iferrors.Is(err,redis.Nil){// key 不存在不是错误是缓存未命中fmt.Println(缓存未命中回源数据库)}elseiferr!nil{returnerr}fmt.Println(读到缓存:,val)// 3. SET NX只在不存在时写入第 13 篇分布式锁的基础ok,err:rdb.SetNX(ctx,user:1001:lock,1,10*time.Second).Result()fmt.Println(抢到锁:,ok,err)// 4. INCR原子计数接口限流、阅读量都靠它pv,err:rdb.Incr(ctx,article:42:pv).Result()fmt.Println(当前阅读量:,pv,err)returnerr}场景 2Hash —— 购物车字段级增删改funcdemoHash(ctx context.Context,rdb*redis.Client)error{key:cart:1001// 5. HSet加入购物车field商品ID, value数量iferr:rdb.HSet(ctx,key,sku_888,2,sku_999,1).Err();err!nil{returnerr}// 6. HIncrBy商品数量 1改字段不用读整个购物车iferr:rdb.HIncrBy(ctx,key,sku_888,1).Err();err!nil{returnerr}// 7. HGetAll结算时取整个购物车cart,err:rdb.HGetAll(ctx,key).Result()fmt.Println(购物车:,cart,err)// 8. HDel删除某个商品returnrdb.HDel(ctx,key,sku_999).Err()}场景 3List —— 最新消息时间线funcdemoList(ctx context.Context,rdb*redis.Client)error{key:timeline:1001// 9. LPush发一条新动态就推到头部rdb.LPush(ctx,key,msg_3,msg_2,msg_1)// 10. LTrim只保留最新 100 条防止无限膨胀bigkey 预防见第 22 篇rdb.LTrim(ctx,key,0,99)// 11. LRange拉第一页最新的 10 条msgs,err:rdb.LRange(ctx,key,0,9).Result()fmt.Println(时间线:,msgs,err)// 12. RPop当简单队列用第 18 篇讲为什么生产要用 Stream 替代returnrdb.RPop(ctx,queue:email).Err()}场景 4Set —— 共同关注与标签funcdemoSet(ctx context.Context,rdb*redis.Client)error{// 13. SAdd记录两个人的关注列表rdb.SAdd(ctx,follows:alice,bob,carol,dave)rdb.SAdd(ctx,follows:eve,carol,dave,frank)// 14. SInter共同关注 交集SISMEMBER 判断单个存在common,err:rdb.SInter(ctx,follows:alice,follows:eve).Result()fmt.Println(共同关注:,common,err)// [carol dave]// 15. SIsMember判断我是否关注了某人isFan,err:rdb.SIsMember(ctx,follows:alice,bob).Result()fmt.Println(alice 关注 bob:,isFan,err)// 16. SCard关注数returnrdb.SCard(ctx,follows:alice).Err()}场景 5ZSet —— 排行榜第 15 篇有完整的工程细节funcdemoZSet(ctx context.Context,rdb*redis.Client)error{key:rank:game:weekly// 17. ZAdd上报分数rdb.ZAdd(ctx,key,redis.Z{Score:2500,Member:player_A},redis.Z{Score:3100,Member:player_B})// 18. ZIncrBy玩家又赢了一局 100 分rdb.ZIncrBy(ctx,key,100,player_A)// 19. ZRevRangeWithScoresTop10分数从高到低top,err:rdb.ZRevRangeWithScores(ctx,key,0,9).Result()fmt.Println(Top10:,top,err)// 20. ZRevRank我的排名从 0 开始展示时 1rank,err:rdb.ZRevRank(ctx,key,player_A).Result()fmt.Println(player_A 排名:,rank1,err)returnerr}三、20 命令速查表收藏点 1命令结构时间复杂度一句话场景SETEXStringO(1)带 TTL 缓存对象GETStringO(1)读缓存redis.Nil 未命中SET NX PXStringO(1)分布式锁地基INCRStringO(1)原子计数HSETHashO(1)写对象字段HGETALLHashO(N)取整个对象大 Hash 慎用见第 22 篇HINCRBYHashO(1)字段级计数HDELHashO(1)删字段LPUSHListO(1)推到头部RPOPListO(1)简单队列出队LRANGEListO(SN)分页拉取LTRIMListO(N)截断防膨胀SADDSetO(1)去重集合SINTERSetO(N×M)共同关注SISMEMBERSetO(1)存在性判断SCARDSetO(1)集合大小ZADDZSetO(logN)上报分数ZINCRBYZSetO(logN)加分ZREVRANGEZSetO(logNM)TopNZREVRANKZSetO(logN)查排名复杂度是选结构的另一半理由凡是不是 O(1)/O(logN) 的命令HGETALL、SINTER、LRANGE 大范围在大 key 上都是定时炸弹第 21/22 篇专门拆。四、容易踩的 3 个坑redis.Nil不是错误Get 未命中返回redis.Nil要用errors.Is(err, redis.Nil)区分没数据和真故障混为一谈会把缓存未命中当成系统异常报警HGetAll 大 Hash 会阻塞10 万字段的 Hash 一次全取就是一次慢查询用 HScan 渐进遍历下一篇List 当队列没有 ACKRPop 拿走就丢了崩溃 丢消息第 18 篇用 Stream 解决。五、总结学命令的正确姿势场景 → 结构 → 命令反过来记不住5 个场景 20 个命令覆盖了日常开发的 80%选结构时看一眼复杂度非 O(1)/O(logN) 的命令在大 key 上有阻塞风险redis.Nil要与真错误分开处理。下一篇预告命令都会用了但有人把 key 起成a、b、c还有人凌晨在 5000 万 key 的库里跑KEYS *——下一篇《一条 KEYS * 把网关拖垮 30 秒——key 命名规范与 SCAN 渐进扫描的 Go 实现》聊聊 key 的工程化管理和安全遍历。