Redis入门:go-redis基本操作与连接池 Redis入门:go-redis基本操作与连接池摘要: 本篇讲解Go操作Redis的核心库go-redis v9包括客户端连接配置、String/Hash/List/Set/ZSet五种数据类型的基本操作、连接池参数调优(PoolSize、MinIdleConns、PoolTimeout)分享连接池耗尽导致请求超时的踩坑经历对比go-redis与redigo的优劣。开篇故事上周五晚上我们一个营销活动的接口开始报超时。日志里全是redis: connection pool timeoutQPS一上来Redis就扛不住。我翻了半天代码连接池配置是默认值PoolSize才是10*runtime.GOMAXPROCS高并发场景下根本不够用。我直接把PoolSize调到200MinIdleConns设成50重新部署后超时消失。go-redis用起来简单但连接池参数不调好生产环境迟早出事。这篇我从安装配置讲起把go-redis v9的基本操作和连接池调优一次性讲透。一、go-redis安装与连接配置go-redis是Redis的Go客户端v9版本做了大量重构API和v8有区别。packagemainimport(contextfmttimegithub.com/redis/go-redis/v9)// 创建Redis客户端配置连接池参数funcnewRedisClient()*redis.Client{rdb:redis.NewClient(redis.Options{Addr:localhost:6379,// Redis地址Password:,// 密码没设就留空DB:0,// 使用的数据库编号PoolSize:200,// 连接池大小默认10*GOMAXPROCSMinIdleConns:20,// 最小空闲连接保持预热PoolTimeout:5*time.Second,// 从池中获取连接的超时IdleTimeout:5*time.Minute,// 空闲连接超时DialTimeout:5*time.Second,// 建立连接超时ReadTimeout:3*time.Second,// 读操作超时MaxRetries:3,// 命令失败重试次数})returnrdb}funcmain(){rdb:newRedisClient()deferrdb.Close()// Ping测试连接ctx:context.Background()pong,err:rdb.Ping(ctx).Result()iferr!nil{fmt.Println(连接失败:,err)return}fmt.Println(连接成功:,pong)// 输出PONG}二、五种数据类型基本操作String与HashString存缓存值和计数器Hash适合存对象。// String类型操作funcstringOps(ctx context.Context,rdb*redis.Client){// 设置键值带过期时间rdb.Set(ctx,name,张三,10*time.Minute)// 获取值区分键不存在和查询出错val,err:rdb.Get(ctx,name).Result()iferrredis.Nil{fmt.Println(键不存在)}elseiferr!nil{fmt.Println(查询出错:,err)}else{fmt.Println(name ,val)}// 自增操作适合做计数器rdb.Set(ctx,counter,0,0)rdb.Incr(ctx,counter)// 1rdb.IncrBy(ctx,counter,5)// 5rdb.Decr(ctx,counter)// -1// SetNX:不存在才设置常用于分布式锁ok,_:rdb.SetNX(ctx,lock:order:1,holder,10*time.Second).Result()fmt.Println(获取锁:,ok)}// Hash类型操作funchashOps(ctx context.Context,rdb*redis.Client){// 一个key下存多个字段rdb.HSet(ctx,user:1,name,李四,age,28,email,lisiexample.com)// 获取单个字段name,_:rdb.HGet(ctx,user:1,name).Result()fmt.Println(name ,name)// 获取所有字段fields,_:rdb.HGetAll(ctx,user:1).Result()fork,v:rangefields{fmt.Printf(%s %s\n,k,v)}rdb.HIncrBy(ctx,user:1,age,1)// 字段自增rdb.HDel(ctx,user:1,email)// 删除字段}List、Set与ZSetList做队列Set做标签去重ZSet做排行榜。// List类型:双向链表适合消息队列funclistOps(ctx context.Context,rdb*redis.Client){rdb.LPush(ctx,messages,msg1,msg2,msg3)// 左推入msg,_:rdb.RPop(ctx,messages).Result()// 右弹出先进先出fmt.Println(弹出:,msg)// 获取范围元素0到-1表示全部vals,_:rdb.LRange(ctx,messages,0,-1).Result()fmt.Println(剩余消息:,vals)// 阻塞式弹出适合消费队列result,_:rdb.BRPop(ctx,5*time.Second,messages).Result()iflen(result)0{fmt.Println(阻塞弹出:,result[1])}}// Set类型:无序集合自动去重funcsetOps(ctx context.Context,rdb*redis.Client){rdb.SAdd(ctx,tags:1,go,redis,backend)rdb.SAdd(ctx,tags:2,go,database,backend)// 交集:共同标签inter,_:rdb.SInter(ctx,tags:1,tags:2).Result()fmt.Println(共同标签:,inter)// [go backend]// 差集:tags:1有但tags:2没有的diff,_:rdb.SDiff(ctx,tags:1,tags:2).Result()fmt.Println(差集:,diff)// [redis]}// ZSet类型:有序集合带分数适合排行榜funczsetOps(ctx context.Context,rdb*redis.Client){rdb.ZAdd(ctx,rank:score,redis.Z{Score:100,Member:p1},redis.Z{Score:85,Member:p2},redis.Z{Score:92,Member:p3})// 按分数从高到低取前10名rank,_:rdb.ZRevRangeWithScores(ctx,rank:score,0,9).Result()fori,z:rangerank{fmt.Printf(第%d名: %s 分数:%.0f\n,i1,z.Member,z.Score)}// 增加分数rdb.ZIncrBy(ctx,rank:score,10,p2)}三、连接池参数调优连接池是go-redis性能的关键参数配错要么连接不够用要么资源浪费。packagemainimport(contextfmtsynctimegithub.com/redis/go-redis/v9)funcmain(){rdb:redis.NewClient(redis.Options{Addr:localhost:6379,PoolSize:200,// 连接池上限默认10*GOMAXPROCSMinIdleConns:20,// 预热连接数建议PoolSize的10%-25%PoolTimeout:4*time.Second,// 池满时等待时间默认ReadTimeout1sIdleTimeout:5*time.Minute,// 空闲连接存活时间MaxRetries:3,// 网络错误自动重试次数})deferrdb.Close()ctx:context.Background()// 并发测试连接池varwg sync.WaitGroupfori:0;i100;i{wg.Add(1)gofunc(nint){deferwg.Done()rdb.Set(ctx,fmt.Sprintf(key:%d,n),value,10*time.Second)}(i)}wg.Wait()// 查看连接池状态stats:rdb.PoolStats()fmt.Printf(总连接: %d, 空闲: %d, 等待次数: %d\n,stats.TotalConns,stats.IdleConns,stats.Waits)}调优看三个指标。stats.Waits多说明PoolSize太小。stats.IdleConns常年接近PoolSize说明配多了。stats.WaitDuration长说明要么加PoolSize要么查Redis本身是否慢。四、独家踩坑:连接池耗尽导致超时上线后发现高峰期接口偶尔超时报错redis: connection pool exhausted。第一反应是Redis扛不住了去看监控CPU才20%。然后看应用日志发现超时集中在一个批量查询接口循环里每次调一次Redis中间还套了time.Sleep(2 * time.Second)。// 问题代码:循环单条查询连接池被打满funcbadExample(ctx context.Context,rdb*redis.Client,ids[]int64){for_,id:rangeids{// 每次Get从池中借一个连接val,_:rdb.Get(ctx,fmt.Sprintf(user:%d,id)).Result()time.Sleep(2*time.Second)// 耗时操作期间连接被占用_val}}真正的问题在PoolSize默认值太小。线上GOMAXPROCS是8默认PoolSize才80高并发下很快借光。修复分两步PoolSize调到200MinIdleConns设50。把循环查询改成Pipeline批量查。// 修复后:用Pipeline批量查询只占一个连接funcfixedExample(ctx context.Context,rdb*redis.Client,ids[]int64)map[int64]string{pipe:rdb.Pipeline()cmds:make(map[int64]*redis.StringCmd,len(ids))for_,id:rangeids{cmds[id]pipe.Get(ctx,fmt.Sprintf(user:%d,id))// 注册命令}pipe.Exec(ctx)// 一次性执行result:make(map[int64]string,len(ids))forid,cmd:rangecmds{ifval,err:cmd.Result();errnil{result[id]val}}returnresult}调完后连接池等待次数从每天几万次降到几十次。经验就是连接池参数一定要根据实际QPS调别用默认值。五、对比分析特性go-redis v9redigoAPI风格链式调用类型安全手动写命令字符串连接池内置参数丰富需手动配置Pool结构集群支持原生Cluster和Sentinel需第三方或自己实现Pipeline内置Pipeline和TxPipeline需手动管理conn.Send自动重试内置MaxRetries需自己实现Context原生支持后期版本才加社区维护活跃官方推荐维护趋缓新项目建议直接选go-redis v9API更现代类型安全避免手写命令出错集群和哨兵开箱即用。redigo轻量但连接管理和错误处理要写更多代码。总结与预告go-redis v9上手简单五种数据类型的API设计清晰。连接池是生产环境关键PoolSize和MinIdleConns一定要根据QPS调优。Pipeline能批量操作就别循环单条查省连接省网络。下一篇讲Redis进阶深入Pipeline原理、事务的WATCH机制和发布订阅模式的实现。

相关新闻

最新新闻

MATLAB线性规划实战:投资组合优化与风险收益权衡

MATLAB线性规划实战:投资组合优化与风险收益权衡

1. 从一道经典的投资题说起:收益与风险的权衡做投资决策,本质上就是在收益和风险之间走钢丝。手里有笔钱,面前摆着几种资产,比如股票、债券、黄金,每种资产的预期年收益率和风险(比如用收益率的方差或标准差…

2026/8/17 6:25:54
SDIO接口深度解析:从存储卡到Wi-Fi模块的通信协议与工程实践

SDIO接口深度解析:从存储卡到Wi-Fi模块的通信协议与工程实践

1. 项目概述:从物理接口到系统集成的桥梁SDIO,全称Secure Digital Input Output,对于很多刚接触嵌入式或移动设备开发的工程师来说,这个名字既熟悉又陌生。熟悉是因为我们几乎每天都能在手机、平板、开发板上看到它的物理载体——…

2026/8/17 6:25:54
MySQL数据库结构探查全攻略:从DESCRIBE到INFORMATION_SCHEMA深度解析

MySQL数据库结构探查全攻略:从DESCRIBE到INFORMATION_SCHEMA深度解析

1. 项目概述:为什么我们需要“看清”数据库?在数据库的日常开发、维护和优化工作中,我们经常需要回答这样一些问题:“这个库里到底有哪些表?”“某个视图的结构是怎样的?”“当初创建这个存储过程时是怎么写…

2026/8/17 6:25:54
Μz:极简快速的zsh插件管理器,提升Shell启动速度与配置效率

Μz:极简快速的zsh插件管理器,提升Shell启动速度与配置效率

1. 先搞清楚 Μz 到底解决了什么,以及它和主流方案的区别如果你在 Linux 或 macOS 上折腾过命令行,大概率听说过 zsh 和它的插件生态。zsh 本身很强,但它的插件管理一直是个麻烦事。你可能用过 oh-my-zsh,它功能全但启动慢&#x…

2026/8/17 6:25:54
Docker BuildKit构建失败:failed to calculate checksum of ref错误排查指南

Docker BuildKit构建失败:failed to calculate checksum of ref错误排查指南

1. 问题场景与核心错误剖析最近在搞容器化部署,尤其是用 Docker BuildKit 构建镜像时,估计不少朋友都遇到过这个让人头大的报错:ERROR: failed to solve: failed to compute cache key: failed to calculate checksum of ref,后面…

2026/8/17 6:25:54
新手7小时完成1000片拼图:一套可复用的工程化分治策略

新手7小时完成1000片拼图:一套可复用的工程化分治策略

最近整理书房,翻出一盒去年买的宝可梦30周年纪念版1000片拼图。盒子崭新,但心里清楚,这玩意儿买回来容易,拼出来难。相信很多人都有过类似经历:被精美的图案吸引,冲动下单,拆开包装面对上千片形…

2026/8/17 6:20:54