Go语言Redis缓存技术实战

Go语言Redis缓存技术实战 Go语言Redis缓存技术实战引言Redis是目前最流行的内存数据库之一被广泛应用于缓存、会话管理、消息队列等场景。Go语言通过go-redis库可以高效地与Redis进行交互。本文将深入探讨Go语言中Redis缓存技术的实战应用。一、环境配置与连接1.1 安装依赖go get github.com/go-redis/redis/v81.2 基本连接配置package main import ( context fmt log github.com/go-redis/redis/v8 ) var ctx context.Background() func main() { // 创建Redis客户端 rdb : redis.NewClient(redis.Options{ Addr: localhost:6379, Password: , // 没有密码时为空 DB: 0, // 使用默认数据库 PoolSize: 10, // 连接池大小 }) // 测试连接 pong, err : rdb.Ping(ctx).Result() if err ! nil { log.Fatalf(Failed to connect to Redis: %v, err) } fmt.Println(Connected to Redis:, pong) }二、基础数据类型操作2.1 String类型func StringOperations(rdb *redis.Client) error { // 设置值 err : rdb.Set(ctx, user:1:name, John, 0).Err() if err ! nil { return err } // 获取值 name, err : rdb.Get(ctx, user:1:name).Result() if err ! nil { return err } fmt.Println(User name:, name) // 设置过期时间 err rdb.Set(ctx, temp:token, abc123, 60*time.Second).Err() if err ! nil { return err } // 原子递增 count, err : rdb.Incr(ctx, counter).Result() if err ! nil { return err } fmt.Println(Counter:, count) return nil }2.2 Hash类型func HashOperations(rdb *redis.Client) error { // 设置Hash字段 err : rdb.HSet(ctx, user:1, map[string]interface{}{ name: John, email: johnexample.com, age: 30, created: time.Now().Unix(), }).Err() if err ! nil { return err } // 获取Hash字段 user, err : rdb.HGetAll(ctx, user:1).Result() if err ! nil { return err } fmt.Println(User:, user) // 获取单个字段 email, err : rdb.HGet(ctx, user:1, email).Result() if err ! nil { return err } fmt.Println(Email:, email) // 递增字段值 newAge, err : rdb.HIncrBy(ctx, user:1, age, 1).Result() if err ! nil { return err } fmt.Println(New age:, newAge) return nil }2.3 List类型func ListOperations(rdb *redis.Client) error { // 从左侧插入 err : rdb.LPush(ctx, queue:tasks, task1, task2, task3).Err() if err ! nil { return err } // 从右侧弹出队列 task, err : rdb.RPop(ctx, queue:tasks).Result() if err ! nil { return err } fmt.Println(Processed task:, task) // 获取列表长度 length, err : rdb.LLen(ctx, queue:tasks).Result() if err ! nil { return err } fmt.Println(Queue length:, length) // 获取列表范围 tasks, err : rdb.LRange(ctx, queue:tasks, 0, -1).Result() if err ! nil { return err } fmt.Println(All tasks:, tasks) return nil }2.4 Set类型func SetOperations(rdb *redis.Client) error { // 添加元素到集合 err : rdb.SAdd(ctx, users:online, user1, user2, user3).Err() if err ! nil { return err } // 检查元素是否存在 exists, err : rdb.SIsMember(ctx, users:online, user1).Result() if err ! nil { return err } fmt.Println(User1 online:, exists) // 获取集合大小 count, err : rdb.SCard(ctx, users:online).Result() if err ! nil { return err } fmt.Println(Online count:, count) // 集合交集 err rdb.SAdd(ctx, users:vip, user1, user4).Err() if err ! nil { return err } vipOnline, err : rdb.SInter(ctx, users:online, users:vip).Result() if err ! nil { return err } fmt.Println(VIP users online:, vipOnline) return nil }2.5 Sorted Set类型func SortedSetOperations(rdb *redis.Client) error { // 添加元素到有序集合 err : rdb.ZAdd(ctx, leaderboard, redis.Z{ Score: 100, Member: player1, }, redis.Z{ Score: 85, Member: player2, }, redis.Z{ Score: 95, Member: player3, }).Err() if err ! nil { return err } // 获取排名从高到低 rank, err : rdb.ZRevRank(ctx, leaderboard, player1).Result() if err ! nil { return err } fmt.Println(Player1 rank:, rank1) // 获取前N名 topPlayers, err : rdb.ZRevRangeWithScores(ctx, leaderboard, 0, 2).Result() if err ! nil { return err } for _, player : range topPlayers { fmt.Printf(%s: %.0f\n, player.Member, player.Score) } // 增加分数 newScore, err : rdb.ZIncrBy(ctx, leaderboard, 10, player2).Result() if err ! nil { return err } fmt.Println(Player2 new score:, newScore) return nil }三、缓存策略实现3.1 缓存与数据库读写模式type User struct { ID int Name string Email string } func GetUser(rdb *redis.Client, db *sql.DB, userID int) (*User, error) { // 先从缓存获取 cacheKey : fmt.Sprintf(user:%d, userID) cachedUser, err : rdb.Get(ctx, cacheKey).Result() if err nil { // 缓存命中 var user User if err : json.Unmarshal([]byte(cachedUser), user); err nil { return user, nil } } // 缓存未命中从数据库获取 var user User err db.QueryRow(SELECT id, name, email FROM users WHERE id ?, userID). Scan(user.ID, user.Name, user.Email) if err ! nil { return nil, err } // 写入缓存 userJSON, _ : json.Marshal(user) rdb.Set(ctx, cacheKey, string(userJSON), 5*time.Minute) return user, nil } func UpdateUser(rdb *redis.Client, db *sql.DB, user *User) error { // 更新数据库 _, err : db.Exec(UPDATE users SET name ?, email ? WHERE id ?, user.Name, user.Email, user.ID) if err ! nil { return err } // 清除缓存 cacheKey : fmt.Sprintf(user:%d, user.ID) rdb.Del(ctx, cacheKey) return nil }3.2 缓存击穿处理var mu sync.Mutex func GetUserWithLock(rdb *redis.Client, db *sql.DB, userID int) (*User, error) { cacheKey : fmt.Sprintf(user:%d, userID) // 尝试获取缓存 cachedUser, err : rdb.Get(ctx, cacheKey).Result() if err nil { var user User json.Unmarshal([]byte(cachedUser), user) return user, nil } // 缓存未命中加锁防止击穿 mu.Lock() defer mu.Unlock() // 再次检查缓存防止重复查询 cachedUser, err rdb.Get(ctx, cacheKey).Result() if err nil { var user User json.Unmarshal([]byte(cachedUser), user) return user, nil } // 查询数据库 var user User err db.QueryRow(SELECT id, name, email FROM users WHERE id ?, userID). Scan(user.ID, user.Name, user.Email) if err ! nil { return nil, err } // 设置缓存 userJSON, _ : json.Marshal(user) rdb.Set(ctx, cacheKey, string(userJSON), 5*time.Minute) return user, nil }四、分布式锁实现4.1 基于Redis的分布式锁func AcquireLock(rdb *redis.Client, lockKey string, timeout time.Duration) (bool, error) { // 使用SET NX获取锁 result, err : rdb.SetNX(ctx, lockKey, locked, timeout).Result() if err ! nil { return false, err } return result, nil } func ReleaseLock(rdb *redis.Client, lockKey string) error { // 使用Lua脚本原子性删除锁 script : if redis.call(GET, KEYS[1]) ARGV[1] then return redis.call(DEL, KEYS[1]) else return 0 end _, err : rdb.Eval(ctx, script, []string{lockKey}, locked).Result() return err } func WithLock(rdb *redis.Client, lockKey string, timeout time.Duration, fn func() error) error { // 获取锁 acquired, err : AcquireLock(rdb, lockKey, timeout) if err ! nil { return err } if !acquired { return fmt.Errorf(failed to acquire lock) } defer ReleaseLock(rdb, lockKey) // 执行临界区代码 return fn() }五、消息队列实现5.1 使用List实现队列type MessageQueue struct { rdb *redis.Client queueKey string } func NewMessageQueue(rdb *redis.Client, queueKey string) *MessageQueue { return MessageQueue{ rdb: rdb, queueKey: queueKey, } } func (mq *MessageQueue) Publish(message string) error { return mq.rdb.RPush(ctx, mq.queueKey, message).Err() } func (mq *MessageQueue) Subscribe() (string, error) { // 阻塞式弹出BLPOP result, err : mq.rdb.BLPop(ctx, 0, mq.queueKey).Result() if err ! nil { return , err } // 返回值格式: [key, value] return result[1], nil } func (mq *MessageQueue) Len() (int64, error) { return mq.rdb.LLen(ctx, mq.queueKey).Result() }5.2 使用Pub/Sub实现发布订阅func PublishMessage(rdb *redis.Client, channel string, message string) error { return rdb.Publish(ctx, channel, message).Err() } func SubscribeChannel(rdb *redis.Client, channel string) { pubsub : rdb.Subscribe(ctx, channel) defer pubsub.Close() for { msg, err : pubsub.ReceiveMessage(ctx) if err ! nil { log.Println(Receive error:, err) continue } fmt.Printf(Received message from %s: %s\n, msg.Channel, msg.Payload) } }六、高级特性6.1 管道操作func PipelineExample(rdb *redis.Client) error { pipe : rdb.Pipeline() // 添加多个命令到管道 pipe.Set(ctx, key1, value1, 0) pipe.Set(ctx, key2, value2, 0) pipe.Incr(ctx, counter) pipe.Get(ctx, key1) // 批量执行 cmds, err : pipe.Exec(ctx) if err ! nil { return err } // 处理结果 for i, cmd : range cmds { fmt.Printf(Command %d result: %v\n, i, cmd.Val()) } return nil }6.2 事务操作func TransactionExample(rdb *redis.Client) error { tx : rdb.TxPipeline() // 添加事务命令 tx.Set(ctx, user:2:name, Alice, 0) tx.Incr(ctx, user:count) // 执行事务 _, err : tx.Exec(ctx) return err }6.3 Lua脚本func LuaScriptExample(rdb *redis.Client) error { script : local key KEYS[1] local value ARGV[1] redis.call(SET, key, value) return redis.call(GET, key) result, err : rdb.Eval(ctx, script, []string{lua:test}, hello).Result() if err ! nil { return err } fmt.Println(Lua result:, result) return nil }七、性能优化与最佳实践7.1 连接池配置func NewRedisClient() *redis.Client { return 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, PoolTimeout: 4 * time.Second, }) }7.2 缓存键设计规范const ( CacheKeyUser user:%d // 用户信息 CacheKeyUserList users:list:%d:%d // 用户列表分页 CacheKeyProduct product:%d // 商品信息 CacheKeyLockPrefix lock: // 锁前缀 ) func GetUserCacheKey(userID int) string { return fmt.Sprintf(CacheKeyUser, userID) }结语Redis作为高性能缓存数据库与Go语言的结合可以构建高效、可靠的后端系统。通过合理使用各种数据类型、缓存策略和高级特性可以充分发挥Redis的优势提升应用性能和可扩展性。希望本文的实战经验能帮助你更好地使用Go语言与Redis进行开发。