← MySQL | Etcd →

Redis - Go Redis 客户端

Redis 是高性能的键值存储数据库,广泛用于缓存、会话管理、消息队列等场景。go-redis 是 Go 最流行的 Redis 客户端库。

📌 核心概念

高性能

内存存储

10 万 + QPS
🔑

数据结构

String/Hash/List/Set

多种类型
🔔

发布订阅

Pub/Sub 消息

实时通信
🔒

分布式锁

Redlock 实现

并发控制

快速开始

📝 连接 Redis

package main

import (
    "context"
    "fmt"
    "github.com/go-redis/redis/v8"
)

func main() {
    // 创建客户端
    rdb := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "", // 无密码
        DB:       0,   // 默认数据库
    })
    
    ctx := context.Background()
    
    // 测试连接
    err := rdb.Ping(ctx).Err()
    if err != nil {
        panic(err)
    }
    fmt.Println("Redis connected!")
    
    // 设置值
    rdb.Set(ctx, "name", "Alice", 0)
    
    // 获取值
    name, _ := rdb.Get(ctx, "name").Result()
    fmt.Printf("Name: %s\n", name)
    
    // 关闭连接
    defer rdb.Close()
}

💡 Redis 要点

  • Context: go-redis v8+ 需要 context
  • 连接池: 客户端内部维护连接池
  • 并发安全: Client 是并发安全的
  • 自动重连: 内置重连机制

字符串操作

📝 String 类型操作

func stringOperations(ctx context.Context, rdb *redis.Client) {
    // 设置
    rdb.Set(ctx, "counter", 0, 0)
    
    // 带过期时间设置
    rdb.Set(ctx, "session:123", "user_data", time.Hour)
    
    // 递增
    rdb.Incr(ctx, "counter")
    rdb.IncrBy(ctx, "counter", 10)
    
    // 递减
    rdb.Decr(ctx, "counter")
    
    // 获取
    val, _ := rdb.Get(ctx, "counter").Int()
    fmt.Printf("Counter: %d\n", val)
    
    // 批量设置/获取
    rdb.MSet(ctx, 
        "key1", "value1",
        "key2", "value2",
    )
    vals, _ := rdb.MGet(ctx, "key1", "key2").Result()
    fmt.Println(vals)
    
    // 获取并删除
    val, _ = rdb.GetDel(ctx, "counter").Int()
}

Hash 类型操作

📝 Hash 类型操作

func hashOperations(ctx context.Context, rdb *redis.Client) {
    // 设置字段
    rdb.HSet(ctx, "user:1001", 
        "name", "Alice",
        "email", "alice@example.com",
        "age", 25,
    )
    
    // 获取单个字段
    name, _ := rdb.HGet(ctx, "user:1001", "name").Result()
    
    // 获取所有字段
    all, _ := rdb.HGetAll(ctx, "user:1001").Result()
    fmt.Printf("User: %+v\n", all)
    
    // 获取多个字段
    vals, _ := rdb.HMGet(ctx, "user:1001", "name", "email").Result()
    
    // 检查字段存在
    exists, _ := rdb.HExists(ctx, "user:1001", "name").Result()
    
    // 删除字段
    rdb.HDel(ctx, "user:1001", "age")
    
    // 获取字段数
    count, _ := rdb.HLen(ctx, "user:1001").Result()
}

List 和 Set 操作

📝 List 和 Set 操作

func listSetOperations(ctx context.Context, rdb *redis.Client) {
    // List: 左侧推入
    rdb.LPush(ctx, "tasks", "task1", "task2", "task3")
    
    // List: 右侧推入
    rdb.RPush(ctx, "queue", "item1", "item2")
    
    // List: 获取范围
    items, _ := rdb.LRange(ctx, "tasks", 0, -1).Result()
    
    // List: 弹出
    task, _ := rdb.LPop(ctx, "tasks").Result()
    
    // Set: 添加成员
    rdb.SAdd(ctx, "tags", "go", "redis", "cache")
    
    // Set: 获取所有成员
    members, _ := rdb.SMembers(ctx, "tags").Result()
    
    // Set: 检查成员
    isMember, _ := rdb.SIsMember(ctx, "tags", "go").Result()
    
    // Set: 集合运算
    rdb.SAdd(ctx, "set1", "a", "b", "c")
    rdb.SAdd(ctx, "set2", "b", "c", "d")
    
    // 交集
    inter, _ := rdb.SInter(ctx, "set1", "set2").Result // [b c]
    
    // 并集
    union, _ := rdb.SUnion(ctx, "set1", "set2").Result // [a b c d]
}

发布订阅

📝 Pub/Sub 消息

func pubSub() {
    ctx := context.Background()
    rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
    
    // 订阅者
    go func() {
        pubsub := rdb.Subscribe(ctx, "channel1")
        defer pubsub.Close()
        
        ch := pubsub.Channel()
        for msg := range ch {
            fmt.Printf("Received: %s\n", msg.Payload)
        }
    }()
    
    // 发布者
    go func() {
        for i := 0; i < 5; i++ {
            rdb.Publish(ctx, "channel1", fmt.Sprintf("Message %d", i)).Err()
            time.Sleep(time.Second)
        }
    }()
    
    time.Sleep(time.Second * 6)
}

分布式锁

📝 Redlock 实现

func distributedLock(ctx context.Context, rdb *redis.Client) {
    // 获取锁
    lock := rdb.Lock(ctx, "mylock", time.Minute)
    
    status, err := lock.Do(ctx)
    if err != nil {
        fmt.Println("Failed to acquire lock")
        return
    }
    
    if !status {
        fmt.Println("Lock not acquired")
        return
    }
    
    defer lock.Unlock(ctx)
    
    // 临界区代码
    fmt.Println("Executing critical section...")
    time.Sleep(time.Second * 2)
}

// 使用 Redis 实现限流
func rateLimit(ctx context.Context, rdb *redis.Client, key string) bool {
    const limit = 100 // 每分钟 100 次
    
    var mu sync.Mutex
    mu.Lock()
    defer mu.Unlock()
    
    val, _ := rdb.Get(ctx, key).Int()
    if val >= limit {
        return false
    }
    
    rdb.Incr(ctx, key)
    rdb.Expire(ctx, key, time.Minute)
    
    return true
}

Pipeline 批量操作

📝 Pipeline 和事务

func pipeline(ctx context.Context, rdb *redis.Client) {
    // Pipeline: 批量操作
    pipe := rdb.Pipeline()
    
    pipe.Set(ctx, "key1", "value1", 0)
    pipe.Set(ctx, "key2", "value2", 0)
    pipe.Get(ctx, "key1")
    
    cmds, _ := pipe.Exec(ctx)
    
    for _, cmd := range cmds {
        fmt.Println(cmd)
    }
    
    // 事务 (MULTI/EXEC)
    err := rdb.Watch(ctx, func(tx *redis.Tx) error {
        val, _ := tx.Get(ctx, "counter").Int()
        
        _, err := tx.Pipelined(ctx, func(pipe redis.Pipeliner) error {
            pipe.Set(ctx, "counter", val+1, 0)
            return nil
        })
        
        return err
    }, "counter")
}

最佳实践

✅ Redis 使用建议

  • 连接复用: 全局单例 Redis 客户端
  • 设置过期: 缓存数据设置 TTL
  • 批量操作: 使用 Pipeline 减少 RTT
  • 键命名: 使用冒号分隔命名空间
  • 监控: 监控内存使用和命中率