go-redis

go-redis 安装与连接

go-redis 是 Go 最流行的 Redis 客户端(v9 为当前主版本,API 与 v8 基本一致):

go get github.com/redis/go-redis/v9
import (
    "context"
    "github.com/redis/go-redis/v9"
)

func main() {
    ctx := context.Background()
    rdb := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "",   // 有密码则填
        DB:       0,    // 默认库
        PoolSize: 100,  // 连接池大小,按并发调整
    })

    if _, err := rdb.Ping(ctx).Result(); err != nil {
        panic("连接失败: " + err.Error())
    }
    defer rdb.Close()
}

基础类型操作

String

rdb.Set(ctx, "name", "John", 0)               // 0 = 永不过期
val, _ := rdb.Get(ctx, "name").Result()
rdb.Incr(ctx, "counter")                      // +1(原子)
rdb.IncrBy(ctx, "counter", 5)                 // +5
rdb.MSet(ctx, "a", "1", "b", "2")             // 批量

Hash

rdb.HSet(ctx, "user:1", "name", "Alice")
name := rdb.HGet(ctx, "user:1", "name").Val()
rdb.HMSet(ctx, "user:1", map[string]interface{}{"age": 30})
all := rdb.HGetAll(ctx, "user:1").Val()

List / Set / ZSet

rdb.LPush(ctx, "tasks", "task1")              // 左推
rdb.RPush(ctx, "tasks", "task2")              // 右推
tasks := rdb.LRange(ctx, "tasks", 0, -1).Val()

rdb.SAdd(ctx, "tags", "go", "redis")
members := rdb.SMembers(ctx, "tags").Val()

rdb.ZAdd(ctx, "rank", redis.Z{Score: 100, Member: "user1"})
top3 := rdb.ZRangeWithScores(ctx, "rank", 0, 2).Val()

高级功能

Pipeline(管道)

一次网络往返发多条命令,批量场景性能提升巨大:

pipe := rdb.Pipeline()
pipe.Set(ctx, "k1", "v1", 0)
pipe.Get(ctx, "k2")
results, _ := pipe.Exec(ctx)

事务(TxPipeline)

事务管道用 MULTI/EXEC 保证原子性(Redis 侧串行执行,没有回滚但要么全执行):

tx := rdb.TxPipeline()
tx.Set(ctx, "balance", 100, 0)
tx.IncrBy(ctx, "balance", 50)
_, err := tx.Exec(ctx)

发布订阅

pubsub := rdb.Subscribe(ctx, "channel1")
msg, err := pubsub.ReceiveMessage(ctx)
fmt.Println(msg.Channel, msg.Payload)

集群与哨兵

// 集群
rdb := redis.NewClusterClient(&redis.ClusterOptions{
    Addrs: []string{"node1:6379", "node2:6379"},
})

// 哨兵
rdb := redis.NewFailoverClient(&redis.FailoverOptions{
    MasterName:    "mymaster",
    SentinelAddrs: []string{"sentinel1:26379"},
})

错误处理:redis.Nil 必须处理

go-redis 最经典的坑:key 不存在时 Get 返回 redis.Nil 错误,它不是网络错误,而是”正常的不存在”:

val, err := rdb.Get(ctx, "key").Result()
if err == redis.Nil {
    fmt.Println("key 不存在")     // 正常分支,别当异常打日志
} else if err != nil {
    panic(err)                    // 真正的错误
}

很多线上误报警都来自把 redis.Nil 当成 error 打日志。

性能与最佳实践

  • 连接池:按并发峰值设置 PoolSize,太小会排队,太大会占内存;
  • 批量操作用 Pipeline:循环里逐条 Set 是反模式;
  • Set 带过期时间:缓存类 key 永远给 TTL,避免内存无限增长;
  • context 传递超时ctx, cancel := context.WithTimeout(...),避免请求挂死;
  • 与 Redigo 对比:Redigo 更底层、无重连封装;go-redis 封装度高、支持集群/哨兵/管道更省心,是现在的主流选择。
滚动至顶部