Redis实战指南:从基础概念到Go语言应用
Redis实战指南:从基础概念到Go语言应用
发布时间: 2025-08-14 (a year ago)
GORedis

Redis实战指南:从基础概念到Go语言应用

Redis(Remote Dictionary Server)是一个开源的内存数据结构存储系统,它不仅可以用作数据库、缓存和消息代理,还支持多种数据结构,如字符串、哈希、列表、集合和有序集合等。今天我们将通过实际的Go语言代码示例来深入了解Redis的使用。

Redis基础数据结构及操作

1. 字符串(String)

字符串是Redis最基本的数据类型,可以存储文本、数字或二进制数据。

GO 复制代码
package main

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

var ctx = context.Background()
var rdb *redis.Client

func init() {
    rdb = redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "", // no password set
        DB:       0,  // use default DB
    })
}

func stringOperations() {
    // 设置键值对
    err := rdb.Set(ctx, "name", "Redis", 0).Err()
    if err != nil {
        panic(err)
    }

    // 获取键值
    val, err := rdb.Get(ctx, "name").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("name:", val)

    // 设置带过期时间的键值对
    err = rdb.Set(ctx, "counter", 0, 1*time.Hour).Err()
    if err != nil {
        panic(err)
    }

    // 自增操作
    err = rdb.Incr(ctx, "counter").Err()
    if err != nil {
        panic(err)
    }

    val, err = rdb.Get(ctx, "counter").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("counter:", val)
}
2. 哈希(Hash)

哈希允许我们将多个键值对存储在一个键下,非常适合存储对象。

GO 复制代码
func hashOperations() {
    // 设置哈希字段
    err := rdb.HSet(ctx, "user:1000", "name", "张三").Err()
    if err != nil {
        panic(err)
    }
    
    err = rdb.HSet(ctx, "user:1000", "email", "zhangsan@example.com").Err()
    if err != nil {
        panic(err)
    }

    // 获取单个字段
    name, err := rdb.HGet(ctx, "user:1000", "name").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("user name:", name)

    // 获取所有字段
    userMap, err := rdb.HGetAll(ctx, "user:1000").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("user info:", userMap)
}
3. 列表(List)

列表是一个有序的字符串列表,支持在两端插入和删除元素。

GO 复制代码
func listOperations() {
    // 向列表左侧插入元素
    err := rdb.LPush(ctx, "tasks", "task1", "task2", "task3").Err()
    if err != nil {
        panic(err)
    }

    // 向列表右侧插入元素
    err = rdb.RPush(ctx, "tasks", "task4").Err()
    if err != nil {
        panic(err)
    }

    // 从左侧弹出元素
    task, err := rdb.LPop(ctx, "tasks").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("popped task:", task)

    // 获取列表长度
    length, err := rdb.LLen(ctx, "tasks").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("tasks count:", length)

    // 获取列表中的所有元素
    tasks, err := rdb.LRange(ctx, "tasks", 0, -1).Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("remaining tasks:", tasks)
}
4. 集合(Set)

集合是无序的字符串集合,支持交集、并集、差集等操作。

GO 复制代码
func setOperations() {
    // 向集合添加元素
    err := rdb.SAdd(ctx, "tags:post:1", "go", "redis", "database").Err()
    if err != nil {
        panic(err)
    }

    err = rdb.SAdd(ctx, "tags:post:2", "go", "web", "api").Err()
    if err != nil {
        panic(err)
    }

    // 检查元素是否在集合中
    isMember, err := rdb.SIsMember(ctx, "tags:post:1", "redis").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("is 'redis' in post1 tags:", isMember)

    // 获取集合中的所有元素
    tags, err := rdb.SMembers(ctx, "tags:post:1").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("post1 tags:", tags)

    // 集合并集操作
    union, err := rdb.SUnion(ctx, "tags:post:1", "tags:post:2").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("union of tags:", union)

    // 集合交集操作
    intersect, err := rdb.SInter(ctx, "tags:post:1", "tags:post:2").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("intersection of tags:", intersect)
}
5. 有序集合(Sorted Set)

有序集合与集合类似,但每个元素都关联一个分数,可以根据分数排序。

GO 复制代码
func sortedSetOperations() {
    // 向有序集合添加元素
    err := rdb.ZAdd(ctx, "leaderboard", 
        &redis.Z{Score: 100, Member: "player1"},
        &redis.Z{Score: 90, Member: "player2"},
        &redis.Z{Score: 95, Member: "player3"}).Err()
    if err != nil {
        panic(err)
    }

    // 增加成员分数
    err = rdb.ZIncrBy(ctx, "leaderboard", 10, "player2").Err()
    if err != nil {
        panic(err)
    }

    // 获取按分数排序的前N个成员
    players, err := rdb.ZRevRangeWithScores(ctx, "leaderboard", 0, 2).Result()
    if err != nil {
        panic(err)
    }
    
    fmt.Println("Leaderboard:")
    for i, player := range players {
        fmt.Printf("%d. %v - %v points\n", i+1, player.Member, player.Score)
    }
}

实际应用场景代码示例

1. 缓存应用
GO 复制代码
func getCachedUser(userID string) (map[string]string, error) {
    // 首先尝试从缓存获取
    cachedUser, err := rdb.HGetAll(ctx, "user:"+userID).Result()
    if err != nil && err != redis.Nil {
        return nil, err
    }

    // 如果缓存中存在数据
    if len(cachedUser) > 0 && cachedUser["name"] != "" {
        fmt.Println("从缓存获取用户信息")
        return cachedUser, nil
    }

    // 模拟从数据库获取数据
    fmt.Println("从数据库获取用户信息")
    user := map[string]string{
        "id":    userID,
        "name":  "张三",
        "email": "zhangsan@example.com",
    }

    // 将数据存入缓存,设置过期时间
    err = rdb.HMSet(ctx, "user:"+userID, user).Err()
    if err != nil {
        return nil, err
    }
    
    // 设置过期时间
    rdb.Expire(ctx, "user:"+userID, 10*time.Minute)

    return user, nil
}
2. 分布式锁
GO 复制代码
func acquireLock(lockKey string, timeout time.Duration) (string, error) {
    // 生成唯一标识
    lockValue := fmt.Sprintf("%d", time.Now().UnixNano())
    
    // 尝试获取锁,设置过期时间防止死锁
    ok, err := rdb.SetNX(ctx, lockKey, lockValue, timeout).Result()
    if err != nil {
        return "", err
    }
    
    if !ok {
        return "", fmt.Errorf("无法获取锁")
    }
    
    return lockValue, nil
}

func releaseLock(lockKey, lockValue string) error {
    // 使用Lua脚本确保原子性
    script := `
        if redis.call("GET", KEYS[1]) == ARGV[1] then
            return redis.call("DEL", KEYS[1])
        else
            return 0
        end
    `
    
    result, err := rdb.Eval(ctx, script, []string{lockKey}, lockValue).Result()
    if err != nil {
        return err
    }
    
    if result.(int64) == 0 {
        return fmt.Errorf("锁已失效或被其他进程持有")
    }
    
    return nil
}

func doWithLock(lockKey string, fn func() error) error {
    lockValue, err := acquireLock(lockKey, 10*time.Second)
    if err != nil {
        return err
    }
    defer releaseLock(lockKey, lockValue)
    
    return fn()
}
3. 消息队列
GO 复制代码
// 生产者
func produceMessage(queueName, message string) error {
    return rdb.LPush(ctx, queueName, message).Err()
}

// 消费者
func consumeMessage(queueName string) (string, error) {
    // 阻塞式弹出消息,超时时间为1秒
    result, err := rdb.BRPop(ctx, 1*time.Second, queueName).Result()
    if err != nil {
        return "", err
    }
    
    // BRPop返回的是包含键名和值的数组,我们只需要值
    if len(result) >= 2 {
        return result[1], nil
    }
    
    return "", fmt.Errorf("无法获取消息")
}

func messageQueueExample() {
    queueName := "task_queue"
    
    // 启动生产者
    go func() {
        for i := 0; i < 5; i++ {
            message := fmt.Sprintf("task_%d", i)
            err := produceMessage(queueName, message)
            if err != nil {
                fmt.Printf("生产消息失败: %v\n", err)
            } else {
                fmt.Printf("生产消息: %s\n", message)
            }
            time.Sleep(1 * time.Second)
        }
    }()
    
    // 启动消费者
    go func() {
        for {
            message, err := consumeMessage(queueName)
            if err != nil {
                if err != redis.Nil {
                    fmt.Printf("消费消息失败: %v\n", err)
                }
                time.Sleep(1 * time.Second)
                continue
            }
            
            fmt.Printf("消费消息: %s\n", message)
            // 模拟处理时间
            time.Sleep(2 * time.Second)
        }
    }()
}

完整示例

GO 复制代码
func main() {
    // 测试连接
    pong, err := rdb.Ping(ctx).Result()
    if err != nil {
        fmt.Println("连接Redis失败:", err)
        return
    }
    fmt.Println("连接成功:", pong)

    // 执行各种操作
    fmt.Println("=== 字符串操作 ===")
    stringOperations()

    fmt.Println("\n=== 哈希操作 ===")
    hashOperations()

    fmt.Println("\n=== 列表操作 ===")
    listOperations()

    fmt.Println("\n=== 集合操作 ===")
    setOperations()

    fmt.Println("\n=== 有序集合操作 ===")
    sortedSetOperations()

    fmt.Println("\n=== 缓存示例 ===")
    user, err := getCachedUser("123")
    if err != nil {
        fmt.Printf("获取用户失败: %v\n", err)
    } else {
        fmt.Printf("获取用户: %+v\n", user)
    }

    // 再次获取同一用户,应该从缓存获取
    user, err = getCachedUser("123")
    if err != nil {
        fmt.Printf("获取用户失败: %v\n", err)
    } else {
        fmt.Printf("获取用户: %+v\n", user)
    }

    fmt.Println("\n=== 分布式锁示例 ===")
    err = doWithLock("test_lock", func() error {
        fmt.Println("执行需要锁保护的操作")
        time.Sleep(3 * time.Second)
        return nil
    })
    if err != nil {
        fmt.Printf("执行锁保护操作失败: %v\n", err)
    }

    fmt.Println("\n=== 消息队列示例 ===")
    messageQueueExample()
    
    // 等待一段时间以便观察消息队列示例的输出
    time.Sleep(10 * time.Second)
}