Redis数据结构与性能优化详解
Redis数据结构与性能优化详解
1. Redis数据类型
1.1 字符串(String)
最基本的数据类型:
// 设置值 rdb.Set(ctx, "name", "John", 0) rdb.SetNX(ctx, "name", "John", 0) // 不存在时设置 rdb.SetEx(ctx, "name", "John", 3600) // 设置过期时间 // 获取值 val, err := rdb.Get(ctx, "name").Result() // 批量操作 rdb.MSet(ctx, "key1", "value1", "key2", "value2") rdb.MGet(ctx, "key1", "key2")1.2 哈希(Hash)
适合存储对象:
// 设置哈希 rdb.HSet(ctx, "user:1", map[string]interface{}{ "name": "John", "age": 30, "email": "john@example.com", }) // 获取字段 name, _ := rdb.HGet(ctx, "user:1", "name").Result() // 获取所有字段 user, _ := rdb.HGetAll(ctx, "user:1").Result() // 字段自增 rdb.HIncrBy(ctx, "user:1", "age", 1)1.3 列表(List)
有序集合,支持栈和队列操作:
// 左插入 rdb.LPush(ctx, "tasks", "task1") rdb.LPush(ctx, "tasks", "task2") // 右插入 rdb.RPush(ctx, "tasks", "task3") // 获取范围 tasks, _ := rdb.LRange(ctx, "tasks", 0, -1).Result() // 弹出 task, _ := rdb.LPop(ctx, "tasks").Result()1.4 集合(Set)
无序不重复:
// 添加 rdb.SAdd(ctx, "tags", "go", "redis", "database") // 获取所有 tags, _ := rdb.SMembers(ctx, "tags").Result() // 判断是否存在 isMember, _ := rdb.SIsMember(ctx, "tags", "go").Result() // 交集 common, _ := rdb.SInter(ctx, "tags1", "tags2").Result()1.5 有序集合(ZSet)
按分数排序:
// 添加 rdb.ZAdd(ctx, "leaderboard", redis.Z{ Score: 100, Member: "player1", }) rdb.ZAdd(ctx, "leaderboard", redis.Z{ Score: 90, Member: "player2", }) // 获取排名 rank, _ := rdb.ZRevRank(ctx, "leaderboard", "player1").Result() // 获取分数 score, _ := rdb.ZScore(ctx, "leaderboard", "player1").Result() // 获取topN topPlayers, _ := rdb.ZRevRangeWithScores(ctx, "leaderboard", 0, 9).Result()2. Go语言Redis客户端
2.1 go-redis客户端
import "github.com/redis/go-redis/v9" rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", DB: 0, PoolSize: 10, }) defer rdb.Close()2.2 连接池配置
rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", DB: 0, PoolSize: 100, // 连接池大小 MinIdleConns: 10, // 最小空闲连接 MaxRetries: 3, // 最大重试次数 DialTimeout: 5 * time.Second, ReadTimeout: 3 * time.Second, WriteTimeout: 3 * time.Second, })3. 性能优化
3.1 管道操作
批量执行命令减少网络开销:
pipe := rdb.Pipeline() pipe.Set(ctx, "key1", "value1", 0) pipe.Set(ctx, "key2", "value2", 0) pipe.Get(ctx, "key1") pipe.Incr(ctx, "counter") cmds, err := pipe.Exec(ctx) for _, cmd := range cmds { // 处理结果 }3.2 事务
pipe := rdb.TxPipeline() pipe.Set(ctx, "key1", "value1", 0) pipe.Set(ctx, "key2", "value2", 0) _, err := pipe.Exec(ctx)3.3 Lua脚本
原子执行:
script := redis.NewScript(` local current = redis.call('GET', KEYS[1]) if current == ARGV[1] then redis.call('SET', KEYS[1], ARGV[2]) return 1 else return 0 end `) result, err := script.Run(ctx, rdb, []string{"key"}, "expected", "new_value").Result()4. 缓存策略
4.1 Cache-Aside模式
func GetUser(ctx context.Context, rdb *redis.Client, db *sql.DB, userID int) (*User, error) { // 1. 先查Redis cacheKey := fmt.Sprintf("user:%d", userID) cached, err := rdb.Get(ctx, cacheKey).Result() if err == nil { var user User json.Unmarshal([]byte(cached), &user) return &user, nil } // 2. 缓存不存在,查数据库 var user User err = db.QueryRowContext(ctx, "SELECT * FROM users WHERE id = ?", userID).Scan(&user) if err != nil { return nil, err } // 3. 写入缓存 data, _ := json.Marshal(user) rdb.Set(ctx, cacheKey, data, 30*time.Minute) return &user, nil }4.2 缓存过期策略
// 设置过期时间 rdb.Set(ctx, "key", "value", 30*time.Minute) // 延长过期时间 rdb.Expire(ctx, "key", 1*time.Hour) // 查看剩余时间 ttl, _ := rdb.TTL(ctx, "key").Result()5. 分布式锁
5.1 实现
func AcquireLock(ctx context.Context, rdb *redis.Client, key string, expiration time.Duration) (bool, error) { result, err := rdb.SetNX(ctx, "lock:"+key, "1", expiration).Result() return result, err } func ReleaseLock(ctx context.Context, rdb *redis.Client, key string) error { return rdb.Del(ctx, "lock:"+key).Err() }5.1 可重入锁
type RedLock struct { rdb *redis.Client keys []string values []string } func (l *RedLock) Lock(ctx context.Context, expiration time.Duration) (bool, error) { for i, key := range l.keys { ok, err := l.rdb.SetNX(ctx, key, l.values[i], expiration).Result() if err != nil || !ok { return false, nil } } return true, nil }6. 消息队列
6.1 发布订阅
// 发布 rdb.Publish(ctx, "channel1", "message1") // 订阅 sub := rdb.Subscribe(ctx, "channel1") defer sub.Close() for msg := range sub.Channel() { fmt.Println(msg.Channel, msg.Payload) }6.2 Stream
// 添加消息 id, err := rdb.XAdd(ctx, &redis.XAddArgs{ Stream: "mystream", Values: map[string]interface{}{"field": "value"}, }).Result() // 读取消息 messages, err := rdb.XRead(ctx, &redis.XReadArgs{ Streams: []string{"mystream"}, Count: 10, }).Result()7. 最佳实践
7.1 键命名规范
业务:对象:属性 user:1:profile order:2023:status session:abc1237.2 内存优化
- 选择合适的数据结构
- 使用压缩存储大值
- 定期清理过期键
- 合理设置maxmemory
7.3 持久化配置
# RDB配置 save 900 1 save 300 10 save 60 10000 # AOF配置 appendonly yes appendfsync everysec8. 总结
Redis作为高性能缓存和消息队列,在现代应用中发挥重要作用。通过合理使用数据结构、管道操作、Lua脚本等特性,可以构建高效的缓存系统和分布式应用。
