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
- 键命名: 使用冒号分隔命名空间
- 监控: 监控内存使用和命中率