本文内容主要是对 go-redis 库官方文档部分内容的翻译,源文档地址:https://redis.uptrace.dev/guide/go-redis.html go-redis 库 Github 地址:https://github.com/go-redis/redis
Introduction
go-redis 是目前最常用的 golang 操作 redis 的库之一。
它支持:
- Redis 3 命令,除了
QUIT、MONITOR、SLOWLOG、SYNC - Automatic connection pooling with circuit breaker support
 - 发布/订阅
 - 事务
 - 管道 和 TxPipeline
 - Scripting
 - Timeouts
 - Redis 哨兵
 - Redis 集群
 - Cluster of Redis Servers without using cluster mode and Redis Sentinel
 - Ring
 - Instrumentation
 - Cache friendly
 - 限流
 - 分布式锁
 
Getting Started
安装
go-redis 仅支持通过 Go modules 使用,所以首先你需要初始化一个 Go module:
$ go mod init github.com/my/repo
如果使用 Redis 6 及以下版本,安装 go-redis/v8:
$ go get github.com/go-redis/redis/v8
如果使用 Redis 7,安装 go-redis/v9:
$ go get github.com/go-redis/redis/v9
连接 Redis 服务器
两种方式
第一种:
import "github.com/go-redis/redis/v8"rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379",Password: "", // no password setDB: 0, // use default DB})
另一种常用的方法是使用连接字符串:
opt, err := redis.ParseURL("redis://<user>:<pass>@localhost:6379/<db>")if err != nil {panic(err)}rdb := redis.NewClient(opt)
通过redis.NewClient实际上会创建一个连接池,go-redis 会自动帮我们维护这个连接池。
上面的代码无法判断是否真的能连接成功,可以用以下代码验证:
// 验证连接if err := client.Ping(context.Background()).Err(); err != nil {panic("Redis 连接失败!\n" + err.Error())}
:::info
当 go-redis 无法连接到 Redis 服务器时,会收到dial tcp: i/o timeout错误消息,例如,当服务器关闭或端口受防火墙保护时。
:::
使用 TLS
要启用 TLS/SSL,需要提供一个空的tls.Config。
如果使用的是私有证书,需要在tls.Config中指定它们。
rdb := redis.NewClient(&redis.Options{TLSConfig: &tls.Config{MinVersion: tls.VersionTLS12,//Certificates: []tls.Certificate{cert}},})
如果得到x509: cannot validate certificate for xxx.xxx.xxx.xxx because it doesn't contain any IP SANs错误,尝试设置ServerName选项:
rdb := redis.NewClient(&redis.Options{TLSConfig: &tls.Config{MinVersion: tls.VersionTLS12,ServerName: "your.domain.com",},})
通过 SSH 通道连接
sshConfig := &ssh.ClientConfig{User: "root",Auth: []ssh.AuthMethod{ssh.Password("password")},HostKeyCallback: ssh.InsecureIgnoreHostKey(),Timeout: 15 * time.Second,}sshClient, err := ssh.Dial("tcp", "remoteIP:22", sshConfig)if err != nil {panic(err)}rdb := redis.NewClient(&redis.Options{Addr: net.JoinHostPort("127.0.0.1", "6379"),Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {return sshClient.Dial(network, addr)},// Disable timeouts, because SSH does not support deadlines.ReadTimeout: -1,WriteTimeout: -1,})
Context
每个 Redis 命令都接受一个上下文,可以使用它来设置超时或传播一些信息,例如跟踪上下文。
ctx := context.Background()
执行命令
可以这样执行一条命令:
val, err := rdb.Get(ctx, "key").Result()fmt.Println(val)
或者,可以保存命令并稍后分别访问值和错误:
get := rdb.Get(ctx, "key")fmt.Println(get.Val(), get.Err())
执行任意命令、自定义命令
val, err := rdb.Do(ctx, "get", "key").Result()if err != nil {if err == redis.Nil {fmt.Println("key does not exists")return}panic(err)}fmt.Println(val.(string))
Do返回一个[Cmd](https://pkg.go.dev/github.com/go-redis/redis/v8#Cmd)结构体,可以通过调用它的一些方法将值转换为各种类型:
// Text is a shortcut for get.Val().(string) with proper error handling.val, err := rdb.Do(ctx, "get", "key").Text()fmt.Println(val, err)
方法的完整列表:
s, err := cmd.Text()flag, err := cmd.Bool()num, err := cmd.Int()num, err := cmd.Int64()num, err := cmd.Uint64()num, err := cmd.Float32()num, err := cmd.Float64()ss, err := cmd.StringSlice()ns, err := cmd.Int64Slice()ns, err := cmd.Uint64Slice()fs, err := cmd.Float32Slice()fs, err := cmd.Float64Slice()bs, err := cmd.BoolSlice()
redis.Nil
如果 Redis 返回(nil)(即 key 不存在),go-redis 会返回redis.Nil错误。
在下面的示例中,我们使用redis.Nil来区分空字符串回复和(nil)回复(key 不存在):
val, err := rdb.Get(ctx, "key").Result()switch {case err == redis.Nil:fmt.Println("key does not exist")case err != nil:fmt.Println("Get failed", err)case val == "":fmt.Println("value is empty")}
GET并不是唯一可能返回redis.Nil的命令,如BLPOP、ZSCORE等也可能返回redis.Nil。
Conn
Conn 表示单个 Redis 连接,而不是连接池。
除非特别需要连续的单个 Redis 连接,否则最好还是使用redis.NewClient的方式。
cn := rdb.Conn(ctx)defer cn.Close()if err := cn.ClientSetName(ctx, "myclient").Err(); err != nil {panic(err)}name, err := cn.ClientGetName(ctx).Result()if err != nil {panic(err)}fmt.Println("client name", name)
连接 Redis 集群
go-redis 自带 Redis Cluster 客户端。redis.ClusterClient底层使用redis.Client与集群中的每个节点进行通信。每个redis.Client都维护一个单独的连接池。
连接 Redis 集群:
import "github.com/go-redis/redis/v8"rdb := redis.NewClusterClient(&redis.ClusterOptions{Addrs: []string{":7000", ":7001", ":7002", ":7003", ":7004", ":7005"},// To route commands by latency or randomly, enable one of the following.//RouteByLatency: true,//RouteRandomly: true,})
迭代分片:
err := rdb.ForEachShard(ctx, func(ctx context.Context, shard *redis.Client) error {return shard.Ping(ctx).Err()})if err != nil {panic(err)}
要迭代主节点,使用ForEachMaster;要迭代从节点,使用ForEachSlave。
更改某些分片的选项:
rdb := redis.NewClusterClient(&redis.ClusterOptions{NewClient: func(opt *redis.Options) *redis.NewClient {user, pass := userPassForAddr(opt.Addr)opt.Username = useropt.Password = passreturn redis.NewClient(opt)},})
go-redis vs redigo
| Feature | go-redis | redigo | 
|---|---|---|
| GitHub stars | 14k+ | 9k+ | 
| 类型安全 | ✔️ | ❌ | 
| 连接池 | 自动的 | 手动的 | 
| 自定义命令 | ✔️ | ✔️ | 
| 高级 发布订阅 API | ✔️ | ❌ | 
| Redis 哨兵客户端 | ✔️ | 使用插件 | 
| Redis 集群客户端 | ✔️ | 使用插件 | 
| Redis Ring | ✔️ | ❌ | 
| OpenTelemetry instrumentation | ✔️ | ❌ | 
两个项目的主要区别在于 go-redis 为每个 Redis 命令提供类型安全的 API,但 redigo 使用类似打印的 API:
// go-redistimeout := time.Second_, err := rdb.Set(ctx, "key", "value", timeout).Result()// redigo_, err := conn.Do("SET", "key", "value", "EX", 1)
当然,go-redis 也支持类似打印的 API:
// go-redis print-like APIok, err := rdb.Do(ctx, "SET" "key", "value", "EX", 1).Bool()
此外,go-redis 自动使用连接池,但 redigo 需要显式管理连接:
// go-redis implicitly uses connection pooling_, err := rdb.Set(...).Result()// redigo requires explicit connection managementconn := pool.Get()_, err := conn.Do(...)conn.Close()
但是如果你需要手动管理连接,go-redis 也允许你这样做:
// go-redis manual connection managementconn := rdb.Conn(ctx)_, err := conn.Set(...).Result()conn.Close()
