必须使用 redis.Pool 而非在 handler 中每次 dial:因 redis.Conn 非并发安全,多 goroutine 共享会 panic 或响应错乱;频繁 dial 导致文件描述符耗尽、Redis CPU 升高;连接池须在 init() 中初始化,Dial 回调内完成 AUTH,handler 中通过 Get()/defer Close() 安全复用。

直接在 Gin 项目里用 redis.Dial() 创建单连接,高并发下必然出问题——不是连接数打满报 can't assign requested address,就是多个 goroutine 争抢同一个 redis.Conn 导致 panic 或响应错乱。必须用 redis.Pool,且初始化时机和使用方式稍有偏差就失效。
为什么不能在 handler 里每次 Dial Redis
Go 的 HTTP handler 是并发执行的,每个请求都调用 redis.Dial(),等于每秒创建几十上百个 TCP 连接。系统文件描述符很快耗尽;Redis 服务端也会因频繁握手、认证、断连而 CPU 拉高;更隐蔽的问题是:redis.Conn 不是并发安全的,Do() 内部包含写+读完整流程,多 goroutine 共享同一连接会响应错位或 panic,而且这种 bug 很难复现。
如何正确初始化 redigo 连接池(Gin 场景)
连接池必须在应用启动时一次性初始化,且 Dial 回调里封装拨号与认证逻辑——不能提前 Dial 好再赋值给全局变量,Go 包级作用域不允许执行函数调用。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
-
redis.Pool应声明为包级变量(如var redisPool *redis.Pool) - 在
init()或main()开头调用&redis.Pool{...}初始化,其中Dial字段必须是闭包函数,内含redis.Dial()和Do("AUTH", ...) - 关键参数建议:
MaxIdle: 20、MaxActive: 100、IdleTimeout: 60 * time.Second;Wait: true表示当池空时阻塞等待而非返回错误 - 不要把密码硬编码进代码,从环境变量或配置文件读取后传入
redis.DialPassword()
Gin handler 中怎么安全获取和释放连接
每个 handler 都要从池里拿新连接,用完立刻归还——不是 Close,是 conn.Close(),它实际是归还到池中;漏掉这步等于连接泄漏。
- 用
conn := redisPool.Get()获取连接,务必检查 err(比如池已关闭或超时) - 所有 Redis 操作用
conn.Do()或conn.Send()/conn.Flush()/conn.Receive(),不要跨 goroutine 复用该conn - 必须用
defer conn.Close(),放在 handler 函数开头最稳妥;如果中间有 return,defer仍能保证归还 - 别用
redisPool.Get().Do(...)一行写法——没地方 defer,连接大概率不归还
常见坑:Gin 中间件或全局结构体里误存连接
有人把 redis.Conn 存进 Gin 的 c.Set("redis", conn) 或自定义 context 结构体,以为能复用,结果是灾难:连接被多个请求/中间件并发访问,状态混乱。
-
redis.Conn是“一次性的”,只属于当前 handler 的当前 goroutine - 中间件里要用 Redis,同样走
redisPool.Get()+defer conn.Close() - 如果想封装工具函数,参数必须是
*redis.Pool,内部自己 Get/Close,绝不暴露redis.Conn出去 - 测试时注意:单元测试里初始化 pool 后,记得在
TestMain结尾调用redisPool.Close(),否则可能残留连接影响后续测试
真正容易被忽略的是 conn.Close() 的语义——它不是关 TCP,而是归还;以及 Dial 回调里必须同步完成 AUTH,否则池里拿到的连接可能未认证就直接用了。

















