redis入门 | 青训营

133 阅读9分钟

Go Redis 入门笔记

1. Redis 简介

Redis(Remote Dictionary Server)是一个高性能的开源键值存储数据库,以内存为核心,支持多种数据结构和持久化方式。它被广泛应用于缓存、会话管理、排行榜、实时分析等领域,因其快速访问、多样化的数据结构和丰富的功能而备受开发者欢迎。

2. Redis 安装与配置

2.1 下载和安装 Redis

在 Go 语言中使用 Redis 需要导入 github.com/go-redis/redis/v8 包,您可以使用 Go 的包管理工具 go get 进行安装:

go get github.com/go-redis/redis/v8

2.2 创建 Redis 客户端

在您的 Go 代码中,使用以下方式创建 Redis 客户端:

import "github.com/go-redis/redis/v8"

func main() {
    // 创建 Redis 客户端
    client := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379", // Redis 服务器地址
        Password: "",               // 密码(如果有的话)
        DB:       0,                // 数据库编号
    })

    // 测试连接
    _, err := client.Ping(ctx).Result()
    if err != nil {
        panic(err)
    }
}

3. Redis 数据类型

Redis 支持多种数据类型,每种类型都有特定的用途和操作。

3.1 字符串(String)

字符串是 Redis 最基本的数据类型,使用 SET 存储字符串,GET 获取字符串。

// 存储字符串
err := client.Set(ctx, "key", "value", 0).Err()
if err != nil {
    panic(err)
}

// 获取字符串
val, err := client.Get(ctx, "key").Result()
if err != nil {
    panic(err)
}
fmt.Println("key:", val)

3.2 列表(List)

使用列表可以存储有序的字符串元素,使用 LPUSHRPUSH 添加元素,LPOPRPOP 移除元素。

// 添加元素到列表
err := client.LPush(ctx, "list_key", "value1", "value2").Err()
if err != nil {
    panic(err)
}

// 从列表左侧弹出元素
val, err := client.LPop(ctx, "list_key").Result()
if err != nil {
    panic(err)
}
fmt.Println("Popped value:", val)

3.3 集合(Set)

集合是无序的字符串集合,使用 SADD 添加元素,SREM 移除元素。

// 添加元素到集合
err := client.SAdd(ctx, "set_key", "value1", "value2").Err()
if err != nil {
    panic(err)
}

// 移除元素
err = client.SRem(ctx, "set_key", "value1").Err()
if err != nil {
    panic(err)
}

3.4 有序集合(Sorted Set)

有序集合存储带分数的字符串元素,使用 ZADD 添加元素,ZREM 移除元素。

// 添加元素到有序集合
err := client.ZAdd(ctx, "sorted_set_key", &redis.Z{Score: 1, Member: "value1"}).Err()
if err != nil {
    panic(err)
}

// 移除元素
err = client.ZRem(ctx, "sorted_set_key", "value1").Err()
if err != nil {
    panic(err)
}

3.5 哈希表(Hash)

哈希表存储字段和值的映射关系,使用 HSet 设置字段值,HGet 获取字段值。

// 设置哈希表字段值
err := client.HSet(ctx, "hash_key", "field1", "value1").Err()
if err != nil {
    panic(err)
}

// 获取哈希表字段值
val, err := client.HGet(ctx, "hash_key", "field1").Result()
if err != nil {
    panic(err)
}
fmt.Println("Field value:", val)

4. Redis 命令

4.1 基本命令

Redis 提供了丰富的命令来操作数据,如 GETSETDELINCRDECR 等。

// 设置值并设置过期时间
err := client.SetEX(ctx, "key", "value", time.Second*10).Err()
if err != nil {
    panic(err)
}

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

4.2 事务和管道

Redis 支持事务操作和管道操作,可以批量执行多个命令。

// 开启事务
pipe := client.TxPipeline()
incr := pipe.Incr(ctx, "counter")
pipe.Expire(ctx, "counter", time.Hour)
_, err := pipe.Exec(ctx)
if err != nil {
    panic(err)
}
fmt.Println("Counter value:", incr.Val())

// 使用管道批量执行命令
pipe := client.Pipeline()
incr := pipe.Incr(ctx, "counter")
pipe.Expire(ctx, "counter", time.Hour)
_, err := pipe.Exec(ctx)
if err != nil {
    panic(err)
}
fmt.Println("Counter value:", incr.Val())

4.3订阅

当谈到 Redis 的发布订阅(Pub/Sub)功能时,它是一种用于实现消息传递模式的机制。发布者(Publisher)发送消息,订阅者(Subscriber)接收消息。这种模式在实时通信、消息推送等场景中非常有用。下面详细讲解 Go 中如何使用 Redis 发布订阅功能:

发布者(Publisher)

package main

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

func main() {
    ctx := context.Background()

    // 创建 Redis 客户端
    client := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379", // Redis 服务器地址
        Password: "",               // 密码(如果有的话)
        DB:       0,                // 数据库编号
    })

    // 发布消息
    message := "Hello, Redis Pub/Sub!"
    err := client.Publish(ctx, "channel_name", message).Err()
    if err != nil {
        panic(err)
    }

    fmt.Println("Message published:", message)
}

订阅者(Subscriber)

package main

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

func main() {
    ctx := context.Background()

    // 创建 Redis 客户端
    client := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379", // Redis 服务器地址
        Password: "",               // 密码(如果有的话)
        DB:       0,                // 数据库编号
    })

    // 订阅消息
    pubsub := client.Subscribe(ctx, "channel_name")
    defer pubsub.Close()

    // 处理接收到的消息
    for {
        msg, err := pubsub.ReceiveMessage(ctx)
        if err != nil {
            panic(err)
        }
        fmt.Println("Received message:", msg.Payload)
    }
}

在以上示例中,发布者使用 Publish 命令向指定频道发送消息,订阅者使用 Subscribe 命令订阅指定频道,然后通过循环不断地使用 ReceiveMessage 方法接收消息。这样,当发布者发送消息到频道时,订阅者将能够接收到该消息。

需要注意的是,订阅者需要在一个独立的进程中运行,以便实时地接收并处理消息。而且,Redis 的发布订阅功能是单向的,即发布者发送消息,订阅者接收消息,没有反向的消息确认机制。

5. Redis 应用场景

Go 语言中使用 Redis 的应用场景多种多样,以下是一些常见的应用场景,以及如何在 Go 语言中利用 Redis 实现这些场景:

1. 缓存

Redis 作为缓存层可以显著提升读取频繁的数据访问性能。在 Go 中,您可以使用 Redis 存储经常访问的数据,如数据库查询结果、计算结果等,以避免重复计算或频繁查询数据库。这可以通过 SETGET 命令来实现。

// 读取缓存
val, err := client.Get(ctx, "cached_data").Result()
if err == redis.Nil {
    // 缓存不存在,需要重新生成数据并存入缓存
    // ...
} else if err != nil {
    panic(err)
} else {
    // 使用缓存数据
}

2. 计数器

利用 Redis 的原子性操作,可以实现高性能的计数器功能。在 Go 中,您可以使用 INCR 命令来实现自增计数器。

// 自增计数器
counter, err := client.Incr(ctx, "page_views").Result()
if err != nil {
    panic(err)
}
fmt.Println("Page views:", counter)

3. 会话管理

在 Web 应用中,您可以使用 Redis 存储用户会话数据,实现分布式的会话管理。在 Go 中,您可以将用户会话数据存储为哈希表或 JSON 字符串,并使用 Redis 的过期功能来自动清理过期的会话数据。

// 设置用户会话数据并设置过期时间
err := client.SetEX(ctx, "session:user_id", sessionData, time.Hour*2).Err()
if err != nil {
    panic(err)
}

// 获取用户会话数据
val, err := client.Get(ctx, "session:user_id").Result()
if err != nil {
    if err == redis.Nil {
        // 会话数据不存在
    } else {
        panic(err)
    }
} else {
    // 使用会话数据
}

4. 队列和任务队列

Redis 的列表数据结构可以用于实现队列和任务队列。在 Go 中,您可以使用列表的 LPUSHRPOP 命令来添加和弹出队列中的元素,实现异步任务处理。

// 添加任务到队列
err := client.LPush(ctx, "task_queue", taskData).Err()
if err != nil {
    panic(err)
}

// 从队列中弹出任务并处理
task, err := client.RPop(ctx, "task_queue").Result()
if err != nil {
    if err == redis.Nil {
        // 队列为空
    } else {
        panic(err)
    }
} else {
    // 处理任务
}

5. 实时排行榜

Redis 的有序集合可以用于实现实时排行榜,如用户积分排行榜、文章热度排行榜等。在 Go 中,您可以使用有序集合的 ZADDZREVRANGE 命令来添加元素和获取排行榜数据。

// 添加用户积分到排行榜
err := client.ZAdd(ctx, "leaderboard", &redis.Z{Score: 100, Member: "user1"}).Err()
if err != nil {
    panic(err)
}

// 获取排行榜前 N 名用户
users, err := client.ZRevRange(ctx, "leaderboard", 0, 9).Result()
if err != nil {
    panic(err)
}
fmt.Println("Top users:", users)

这些只是 Go 语言中使用 Redis 的一些常见应用场景,实际上 Redis 还可以在更多领域发挥作用,如实时聊天、发布订阅系统、地理位置服务等。选择合适的数据结构和命令,可以让您在 Go 项目中充分发挥 Redis 的性能和功能,提升应用的性能和用户体验。

6. Redis 数据结构原理

当谈到 Redis 数据结构原理时,我们需要了解 Redis 是如何实现不同数据类型的存储和操作的。Redis 在内部使用多种数据结构来支持各种数据类型,这些数据结构的选择和设计使得 Redis 具备高性能和灵活性。以下是 Redis 常见数据结构的详细讲解:

字符串(String)

Redis 中的字符串数据结构相对简单,它是一个字节数组,存储着任意二进制数据。字符串的键映射到字符串值,您可以使用 SET 命令设置字符串值,使用 GET 命令获取字符串值。

在内部,Redis 使用简单动态字符串(simple dynamic string,SDS)实现字符串。SDS 是一个带有长度信息的字节数组,它的长度可以动态变化,但不会造成频繁的内存分配。SDS 的设计使得字符串的操作非常高效,例如追加、截取等操作都可以在 O(1) 时间内完成。

列表(List)

列表数据结构在 Redis 内部使用双向链表实现,每个节点存储一个值,且每个节点都有指向前一个节点和后一个节点的指针。这种结构使得在两端进行添加和删除操作非常高效。

Redis 为列表提供了多种操作命令,例如 LPUSHRPUSH 可以在列表的左侧和右侧添加元素,LPOPRPOP 可以从列表的左侧和右侧弹出元素。通过这些操作,可以实现队列和栈等数据结构。

集合(Set)

集合数据结构在 Redis 中使用哈希表实现,它存储着一组不重复的字符串元素。哈希表的键映射到特定的桶,每个桶存储着一个元素。

集合的操作命令包括 SADD 添加元素,SREM 移除元素,SISMEMBER 判断元素是否存在等。由于使用了哈希表,集合的添加、删除和查找操作的时间复杂度都是 O(1)。

有序集合(Sorted Set)

有序集合在 Redis 中使用跳跃表和哈希表实现,它类似于集合,但每个元素都关联一个分数,通过分数来排序。

跳跃表(skip list)是一种有序数据结构,它允许快速的插入、删除和查找操作。在 Redis 中,有序集合的元素被存储在跳跃表中,而元素与分数的映射则存储在哈希表中。

有序集合的操作命令包括 ZADD 添加元素和分数,ZREM 移除元素,ZRANGE 获取指定范围的元素等。由于使用了跳跃表,有序集合在插入、删除和查找操作方面的性能都非常出色。

哈希表(Hash)

哈希表数据结构在 Redis 中用于存储键值对,它类似于其他编程语言中的字典或映射。

Redis 的哈希表使用类似于集合的方式来存储键,即键与值的映射关系被存储在哈希表的桶中。当哈希表的键数量较小时,Redis 使用小的字典实现,当键数量增大时,会进行自动扩展。

哈希表的操作命令包括 HSET 设置字段值,HGET 获取字段值,HDEL 删除字段,HGETALL 获取全部字段等。

以上只是 Redis 数据结构原理的一部分,Redis 还支持其他数据结构如位图、HyperLogLog 等。了解这些数据结构的内部实现可以帮助您更好地理解 Redis 的性能和功能,从而更有效地使用 Redis 来解决实际问题。