Go Redis 调试

连接池大小

go-redis 底层维护了一个连接池,不需要手动管理。默认情况下, go-redis 连接池大小为 runtime.GOMAXPROCS * 10,在大多数情况下默认值已经足够使用,且设置太大的连接池几乎没有什么用,可以在 配置项 中调整连接池数量:

rdb := redis.NewClient(&redis.Options{ PoolSize: 1000, }) 

这里介绍一下连接池的配置(连接池配置全部继承自 配置项 ):

type Options struct { // 创建网络连接时调用的函数 Dialer func(context.Context) (net.Conn, error) // 连接池模式,FIFO和LIFO模式 PoolFIFO bool // 连接池大小 PoolSize int // 从连接池获取连接超时时间(如果所有连接都繁忙,等待的时间) PoolTimeout time.Duration // 最小空闲连接数,受PoolSize限制 MinIdleConns int // 最大空闲连接数,多余会被关闭 MaxIdleConns int // 每个连接最大空闲时间,如果超过了这个时间会被关闭 ConnMaxIdleTime time.Duration // 连接最大生命周期 ConnMaxLifetime time.Duration } 

连接池的结构:

type ConnPool struct { // 配置项信息	cfg *Options // 创建网络连接错误次数 // 如果超过了 `Options.PoolSize` 次,再创建新连接时,会直接返回 `lastDialError` 错误, // 同时会进行间隔1秒的探活操作,如果探活成功,会把 `dialErrorsNum` 重新设置为0	dialErrorsNum uint32 // atomic // 用于记录最后一次创建新连接的错误信息	lastDialError atomic.Value // 长度为 `Options.PoolSize` 的chan // 从连接池获取连接时向chan放入一个数据,返还连接时从chan中取出一个数据 // 如果chan已满,则证明连接池所有连接已经都被使用,需要等待	queue chan struct{} // 连接池并发安全锁	connsMu sync.Mutex // 所有连接列表	conns []*Conn // 空闲连接	idleConns []*Conn // 当前连接池大小,最大为 `Options.PoolSize`	poolSize int // 空闲连接长度	idleConnsLen int // 统计状态的	stats Stats // 连接池是否被关闭的 atomic 值,1-被关闭	_closed uint32 // atomic } 

连接池超时错误:redis: connection pool timeout

当连接池中没有空闲连接时,可能因为超过了 Options.PoolTimeout 时间而收到此错误,如果你在使用 Pub/Subredis.Conn,请确保不再使用它们时正确释放 PubSub/Conn 占用的网络资源。

当 Redis 处理命令的速度太慢并且池中的所有连接被阻塞的时间超过 PoolTimeout 持续时间时,也可能会遇到该错误。

超时

如果你使用 context.Context 处理超时,但也不要禁用 DialTimeoutReadTimeoutWriteTimeout ,因为 go-redis 会在不使用 context.Context 的情况下执行一些后台检查,这些检查依赖这些超时配置项。

请注意:net.Conn 依赖 Deadline 而不是 ctx

context.Context 的超时时间不要设置太短,因为当 context.Context超时,连接池无法确认连接是否还能正常使用,后面可能还会接收到数据,这样的连接不能被复用,只能丢弃并打开新的网络连接。在网络出现缓慢、丢包、redis 服务器执行消耗过多时间时, 将出现大量连接被丢弃、新建连接,这样连接池也就失去了意义,且情况会越来越恶化。

你可以查看 Go Context timeouts can be harmful在新窗口打开 (英文版) 这篇文章了解更多。

context 是一种控制超时的方式,但并不是所有场景都适用它。

大量的连接

在高负载下一些命令会超时,go-redis 会关闭这样的连接,因为它们后面还能接收到一些数据,不能被复用。关闭的连接会进入 TIME_WAIT 状态,通常是一分钟左右,你可以查看它:

cat /proc/sys/net/ipv4/tcp_fin_timeout 60 

你可以增加读/写超时或升级您的服务器以处理更多流量,还可以增加打开连接的最大数量,但这样做仅仅是改善这个情况,对改善性能或网络效率没什么用。

这里有 处理 TCP TIME-WAIT在新窗口打开 的一些建议。

管道

go-redis 大部分时间都在等待网络写入/读取的操作,因此你可以使用 pipelines 一次发送/读取多个命令来提高性能。

缓存

除了 pipelines,你也可以考虑使用 本地缓存 来提高性能,参考 TinyLFU

硬件

应该确保服务器有良好的网络和高速缓存的 CPU。如果你有多个 CPU 内核,请考虑在单个服务器上运行多个 Redis 实例。

请参见 影响 redis 性能的因素在新窗口打开 更多细节。