本文介绍使用 Go 客户端通过代理模式和直连模式连接腾讯云分布式缓存数据库实例的示例。
准备工作
环境要求
Go 1.18及以上版本。
Go Modules 包管理。
安装客户端
推荐使用 go-redis 客户端(v9及以上版本):
go get github.com/redis/go-redis/v9
获取连接信息
1. 登录 分布式缓存数据库控制台。
2. 在实例列表中,单击目标实例 ID,进入实例详情页。
3. 在网络信息区域,获取实例的连接地址和端口号。
代理模式:获取 VIP 地址和端口(默认6379)。
直连模式:获取各分片节点地址和端口(默认6379)。
4. 在配置信息区域,获取实例的连接密码。代理模式与直连模式的认证方式不同:
代理模式:默认账号使用
实例 ID:密码 格式,自定义账号使用 用户名@密码 格式;免密认证实例无需密码。直连模式:复用 Redis 原生 ACL 认证机制,不支持
用户名@密码 拼接格式,支持以下两种认证方式:默认账号:仅需密码,即
AUTH password。自定义账号:需同时指定账号名与密码,即
AUTH account password。代理模式连接示例
代理模式下,客户端通过 Proxy 代理层提供的统一 VIP 地址连接实例,Proxy 自动处理请求路由和故障转移,客户端无需感知后端拓扑变化。
连接池示例(推荐)
package mainimport ("context""fmt""time""github.com/redis/go-redis/v9")func main() {ctx := context.Background()// 创建 Redis 客户端rdb := redis.NewClient(&redis.Options{Addr: "192.xx.xx.2:6379", // 实例 VIP 地址Password: "crs-xxxx:密码", // 默认账号密码格式:实例ID:密码DB: 0, // 数据库编号PoolSize: 200, // 连接池大小MinIdleConns: 50, // 最小空闲连接数MaxIdleConns: 200, // 最大空闲连接数DialTimeout: 2 * time.Second, // 连接超时ReadTimeout: 2 * time.Second, // 读取超时WriteTimeout: 2 * time.Second, // 写入超时PoolTimeout: 3 * time.Second, // 连接池获取超时ConnMaxIdleTime: 30 * time.Minute, // 空闲连接最大存活时间})// 测试连接pong, err := rdb.Ping(ctx).Result()if err != nil {panic(fmt.Sprintf("Redis connection failed: %v", err))}fmt.Println("Redis connected:", pong)// 写入数据err = rdb.Set(ctx, "name", "腾讯云分布式缓存数据库", 0).Err()if err != nil {panic(err)}// 读取数据val, err := rdb.Get(ctx, "name").Result()if err != nil {panic(err)}fmt.Println("get name:", val)// 设置带过期时间的 Keyerr = rdb.Set(ctx, "session:user123", "session_data", 3600*time.Second).Err()if err != nil {panic(err)}// 哈希操作err = rdb.HSet(ctx, "user:1001", map[string]interface{}{"name": "张三","age": "28",}).Err()if err != nil {panic(err)}name, err := rdb.HGet(ctx, "user:1001", "name").Result()if err != nil {panic(err)}fmt.Println("user name:", name)// 列表操作err = rdb.LPush(ctx, "queue:tasks", "task1", "task2", "task3").Err()if err != nil {panic(err)}task, err := rdb.RPop(ctx, "queue:tasks").Result()if err != nil {panic(err)}fmt.Println("dequeued task:", task)// Pipeline 批量操作pipe := rdb.Pipeline()pipe.Set(ctx, "key1", "value1", 0)pipe.Set(ctx, "key2", "value2", 0)pipe.Set(ctx, "key3", "value3", 0)pipe.Get(ctx, "key1")pipe.Get(ctx, "key2")pipe.Get(ctx, "key3")cmds, err := pipe.Exec(ctx)if err != nil {panic(err)}for _, cmd := range cmds {fmt.Printf("pipeline result: %v\\n", cmd)}// 关闭连接rdb.Close()}
自定义账号连接示例
package mainimport ("context""fmt""time""github.com/redis/go-redis/v9")func main() {ctx := context.Background()rdb := redis.NewClient(&redis.Options{Addr: "192.xx.xx.2:6379",Password: "myuser@MyPassword", // 代理模式自定义账号:用户名@密码整体作为密码传入,不单独设置 Username(区别于直连模式 ACL 的账号名/密码分离传参)DB: 0,PoolSize: 200,MinIdleConns: 50,DialTimeout: 2 * time.Second,ReadTimeout: 2 * time.Second,WriteTimeout: 2 * time.Second,})pong, err := rdb.Ping(ctx).Result()if err != nil {panic(err)}fmt.Println("Redis connected:", pong)err = rdb.Set(ctx, "key", "value", 0).Err()if err != nil {panic(err)}val, err := rdb.Get(ctx, "key").Result()if err != nil {panic(err)}fmt.Println("get key:", val)rdb.Close()}
SSL 加密连接示例
package mainimport ("context""crypto/tls""crypto/x509""fmt""os""time""github.com/redis/go-redis/v9")func main() {ctx := context.Background()// 加载 CA 证书caCert, err := os.ReadFile("/path/to/ca.pem")if err != nil {panic(err)}caCertPool := x509.NewCertPool()caCertPool.AppendCertsFromPEM(caCert)// 配置 TLStlsConfig := &tls.Config{RootCAs: caCertPool,InsecureSkipVerify: false, // 验证服务端证书}rdb := redis.NewClient(&redis.Options{Addr: "192.xx.xx.2:6379",Password: "crs-xxxx:密码",DB: 0,PoolSize: 200,MinIdleConns: 50,DialTimeout: 2 * time.Second,ReadTimeout: 2 * time.Second,WriteTimeout: 2 * time.Second,TLSConfig: tlsConfig,})pong, err := rdb.Ping(ctx).Result()if err != nil {panic(err)}fmt.Println("SSL Redis connected:", pong)err = rdb.Set(ctx, "ssl_test", "encrypted_connection", 0).Err()if err != nil {panic(err)}val, err := rdb.Get(ctx, "ssl_test").Result()if err != nil {panic(err)}fmt.Println("ssl_test:", val)rdb.Close()}
直连模式连接示例
直连模式下,客户端使用 Redis Cluster 协议直接连接各分片节点,本地处理 MOVED/ASK 重定向,可直接使用社区标准的 Redis Cluster 客户端接入。
说明:
直连模式仅适用于集群架构实例。
需使用 go-redis 的 ClusterClient。
需在控制台开启直连模式并获取各分片节点的连接地址。
认证方式复用 Redis 原生 ACL 机制:默认账号仅传密码,自定义账号需分别传入账号名与密码,不支持
用户名@密码 拼接格式。直连模式暂不支持 SSL 加密连接。
ClusterClient 基础连接示例
package mainimport ("context""fmt""time""github.com/redis/go-redis/v9")func main() {ctx := context.Background()// 创建集群客户端rdc := redis.NewClusterClient(&redis.ClusterOptions{Addrs: []string{"192.xx.xx.2:6379", // 分片节点地址"192.xx.xx.3:6379","192.xx.xx.4:6379",},Password: "password", // 直连模式默认账号仅传密码,不带实例 ID 前缀PoolSize: 200, // 每个节点连接池大小MinIdleConns: 50, // 每个节点最小空闲连接MaxRedirects: 3, // 最大重定向次数DialTimeout: 2 * time.Second,ReadTimeout: 2 * time.Second,WriteTimeout: 2 * time.Second,PoolTimeout: 3 * time.Second,RouteByLatency: false, // 是否按延迟路由RouteRandomly: false, // 是否随机路由})// 测试连接err := rdc.ForEachShard(ctx, func(ctx context.Context, shard *redis.Client) error {return shard.Ping(ctx).Err()})if err != nil {panic(fmt.Sprintf("Cluster connection failed: %v", err))}fmt.Println("Redis Cluster connected")// 写入数据(客户端自动路由到正确的分片)err = rdc.Set(ctx, "cluster:key1", "value1", 0).Err()if err != nil {panic(err)}err = rdc.Set(ctx, "cluster:key2", "value2", 0).Err()if err != nil {panic(err)}err = rdc.Set(ctx, "cluster:key3", "value3", 0).Err()if err != nil {panic(err)}// 读取数据val1, err := rdc.Get(ctx, "cluster:key1").Result()if err != nil {panic(err)}fmt.Println("key1:", val1)val2, err := rdc.Get(ctx, "cluster:key2").Result()if err != nil {panic(err)}fmt.Println("key2:", val2)// 哈希操作err = rdc.HSet(ctx, "user:{1001}", map[string]interface{}{"name": "张三","age": "28",}).Err()if err != nil {panic(err)}name, err := rdc.HGet(ctx, "user:{1001}", "name").Result()if err != nil {panic(err)}fmt.Println("user name:", name)// 使用 Hash Tag 确保相关 Key 路由到同一分片err = rdc.Set(ctx, "{order:1001}:status", "paid", 0).Err()if err != nil {panic(err)}err = rdc.Set(ctx, "{order:1001}:amount", "99.00", 0).Err()if err != nil {panic(err)}err = rdc.Set(ctx, "{order:1001}:time", "2025-01-01 10:00:00", 0).Err()if err != nil {panic(err)}status, err := rdc.Get(ctx, "{order:1001}:status").Result()if err != nil {panic(err)}fmt.Println("order status:", status)// Pipeline 操作(同一 Hash Tag 的 Key)pipe := rdc.Pipeline()pipe.Set(ctx, "{batch}:k1", "v1", 0)pipe.Set(ctx, "{batch}:k2", "v2", 0)pipe.Set(ctx, "{batch}:k3", "v3", 0)pipe.Get(ctx, "{batch}:k1")pipe.Get(ctx, "{batch}:k2")pipe.Get(ctx, "{batch}:k3")cmds, err := pipe.Exec(ctx)if err != nil {panic(err)}for _, cmd := range cmds {fmt.Printf("pipeline: %v\\n", cmd)}// 关闭连接rdc.Close()}
自定义账号(ACL)连接示例
直连模式复用 Redis 原生 ACL 认证机制,使用自定义账号时需分别传入账号名与密码,不支持
用户名@密码 拼接格式。package mainimport ("context""fmt""time""github.com/redis/go-redis/v9")func main() {ctx := context.Background()// ===== ACL 认证机制:账号名与密码是两个独立字段 =====// 直连模式的多账号能力基于 Redis 原生 ACL:控制台创建的每个自定义账号// 都是一个独立的 ACL user,认证时客户端向节点发送 AUTH <account> <password>。// 因此需将 Username 与 Password 分别传入(等价于执行 AUTH account password),// 不要写成代理模式的 "myuser@MyPassword" 拼接格式。rdc := redis.NewClusterClient(&redis.ClusterOptions{Addrs: []string{"192.xx.xx.2:6379","192.xx.xx.3:6379","192.xx.xx.4:6379",},Username: "myuser", // ACL 账号名 → AUTH accountPassword: "MyPassword", // 账号密码 → AUTH password(纯密码,无需拼接)PoolSize: 200,MinIdleConns: 50,MaxRedirects: 3,DialTimeout: 2 * time.Second,ReadTimeout: 2 * time.Second,WriteTimeout: 2 * time.Second,})err := rdc.Set(ctx, "key", "value", 0).Err()if err != nil {panic(err)}val, err := rdc.Get(ctx, "key").Result()if err != nil {panic(err)}fmt.Println("get key:", val)rdc.Close()}
连接参数说明
参数 | 说明 | 推荐值 |
Addr | 实例连接地址。代理模式为 VIP 地址,直连模式为分片节点地址 | - |
Password | 认证密码 | 默认账号格式: 实例 ID:密码 |
PoolSize | 连接池大小(Cluster 模式下为每个节点的连接数) | 200 |
MinIdleConns | 最小空闲连接数 | 50 |
DialTimeout | 连接超时 | 2 秒 |
ReadTimeout | 读取超时 | 2 秒 |
WriteTimeout | 写入超时 | 2 秒 |
PoolTimeout | 连接池获取超时 | 3 秒 |
MaxRedirects | 最大重定向次数(Cluster 模式) | 3 |
常见问题
连接超时
确认客户端所在 CVM 与分布式缓存数据库实例在同一 VPC 内。
确认安全组规则已放通对应端口。
确认连接地址和端口正确。
认证失败
代理模式:默认账号使用
实例 ID:密码,自定义账号使用 用户名@密码。直连模式:复用 Redis ACL 认证,默认账号仅传密码(
AUTH password),自定义账号需分别传入账号名与密码(AUTH account password),不支持 用户名@密码 拼接格式。确认密码中无特殊字符导致解析异常。
直连模式 MOVED 错误
确认使用
NewClusterClient 而非 NewClient。确认已在控制台开启直连模式。
确认 Addrs 地址为分片节点地址而非 VIP 地址。
连接池耗尽
检查
PoolSize 是否设置合理。确认
PoolTimeout 设置合理,避免长时间阻塞。检查业务代码中是否有连接泄漏(未正确关闭)。