Redis 十大经典使用场景 - Go 语言实战指南

简介: 本文详解 Redis 在 Go 中的 10 大核心应用场景:缓存、会话存储、限流、排行榜、消息队列、发布订阅、实时分析、分布式锁、地理位置、购物车,并提供完整可运行代码与最佳实践,助你高效构建高性能应用。(239字)

📦 环境准备

# 安装 Redis Go 客户端
go get github.com/redis/go-redis/v9

# 启动 Redis(Docker)
docker run -d -p 6379:6379 redis:latest
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "time"

    "github.com/redis/go-redis/v9"
)

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

func init() {
   
    rdb = redis.NewClient(&redis.Options{
   
        Addr: "localhost:6379",
    })
}

1️⃣ 缓存(Caching)🔥

最经典的使用场景,将频繁访问的数据存入内存,减轻数据库压力 [[1]]。

工作原理

┌─────────────┐     ┌──────────┐     ┌──────────┐
│   客户端     │ ──> │  Redis   │ ──> │ 数据库    │
│             │ <── │ (缓存命中)│     │          │
└─────────────┘     └──────────┘     └──────────┘

Go 代码实现

// 缓存旁路模式(Cache-Aside)
func getUserWithCache(userID string) (*User, error) {
   
    key := fmt.Sprintf("user:%s", userID)

    // 1. 先查 Redis
    data, err := rdb.Get(ctx, key).Result()
    if err == nil {
   
        // 缓存命中
        var user User
        json.Unmarshal([]byte(data), &user)
        return &user, nil
    }

    // 2. 缓存未命中,查数据库
    user, err := getUserFromDB(userID)
    if err != nil {
   
        return nil, err
    }

    // 3. 写入 Redis,设置 TTL
    userData, _ := json.Marshal(user)
    rdb.Set(ctx, key, userData, 30*time.Minute)

    return user, nil
}

// 带过期时间的缓存
func setCache(key string, value interface{
   }, ttl time.Duration) error {
   
    data, _ := json.Marshal(value)
    return rdb.Set(ctx, key, data, ttl).Err()
}

💡 最佳实践:设置合理的 TTL 避免脏数据,推荐使用 5-30 分钟


2️⃣ 会话存储(Session Store)🔐

现代 Web 应用需要无状态会话管理,Redis 是完美选择 [[2]]。

Go 代码实现

type Session struct {
   
    UserID    string    `json:"user_id"`
    Username  string    `json:"username"`
    LoginAt   time.Time `json:"login_at"`
    ExpiresAt time.Time `json:"expires_at"`
}

// 创建会话
func createSession(userID, username string) (string, error) {
   
    sessionID := generateUUID()
    session := &Session{
   
        UserID:    userID,
        Username:  username,
        LoginAt:   time.Now(),
        ExpiresAt: time.Now().Add(24 * time.Hour),
    }

    key := fmt.Sprintf("session:%s", sessionID)
    data, _ := json.Marshal(session)

    // 设置 24 小时过期
    err := rdb.Set(ctx, key, data, 24*time.Hour).Err()
    return sessionID, err
}

// 获取会话
func getSession(sessionID string) (*Session, error) {
   
    key := fmt.Sprintf("session:%s", sessionID)
    data, err := rdb.Get(ctx, key).Result()
    if err != nil {
   
        return nil, err
    }

    var session Session
    json.Unmarshal([]byte(data), &session)
    return &session, nil
}

// 删除会话(登出)
func deleteSession(sessionID string) error {
   
    key := fmt.Sprintf("session:%s", sessionID)
    return rdb.Del(ctx, key).Err()
}

⚠️ 注意:生产环境建议配置 Redis 持久化(RDB/AOF)或主从复制


3️⃣ 限流(Rate Limiting)🛑

保护 API 不被滥用,固定窗口滑动窗口算法

Go 代码实现(令牌桶算法)

// 基于 INCR 的固定窗口限流
func rateLimit(userID string, limit int, window time.Duration) bool {
   
    key := fmt.Sprintf("ratelimit:%s", userID)

    // 原子递增
    count, _ := rdb.Incr(ctx, key).Result()

    // 第一次设置过期时间
    if count == 1 {
   
        rdb.Expire(ctx, key, window)
    }

    return count <= int64(limit)
}

// 滑动窗口限流(更精确)
func slidingWindowRateLimit(userID string, limit int, window time.Duration) bool {
   
    key := fmt.Sprintf("ratelimit:sliding:%s", userID)
    now := time.Now().UnixNano()
    windowStart := now - window.Nanoseconds()

    // 移除过期请求
    rdb.ZRemRangeByScore(ctx, key, "0", fmt.Sprintf("%d", windowStart))

    // 添加当前请求
    rdb.ZAdd(ctx, key, redis.Z{
   Score: float64(now), Member: fmt.Sprintf("%d", now)})

    // 设置过期
    rdb.Expire(ctx, key, window)

    // 统计请求数
    count, _ := rdb.ZCard(ctx, key).Result()
    return count <= int64(limit)
}
// 中间件使用示例
func RateLimitMiddleware(limit int, window time.Duration) func(http.Handler) http.Handler {
   
    return func(next http.Handler) http.Handler {
   
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   
            userID := r.RemoteAddr
            if !rateLimit(userID, limit, window) {
   
                http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
                return
            }
            next.ServeHTTP(w, r)
        })
    }
}

4️⃣ 排行榜(Leaderboard)🏆

使用 Sorted Set 实现实时排名,游戏场景必备

Go 代码实现

// 添加玩家分数
func addPlayerScore(playerID string, score int64) error {
   
    return rdb.ZAdd(ctx, "leaderboard:global", 
        redis.Z{
   Score: float64(score), Member: playerID}).Err()
}

// 获取玩家排名
func getPlayerRank(playerID string) (int64, error) {
   
    // ZRANK 返回从 0 开始的排名,+1 得到实际排名
    rank, err := rdb.ZRevRank(ctx, "leaderboard:global", playerID).Result()
    if err != nil {
   
        return 0, err
    }
    return rank + 1, nil
}

// 获取 Top N 玩家
func getTopPlayers(n int64) ([]redis.Z, error) {
   
    return rdb.ZRevRangeWithScores(ctx, "leaderboard:global", 0, n-1).Result()
}

// 获取玩家分数
func getPlayerScore(playerID string) (float64, error) {
   
    return rdb.ZScore(ctx, "leaderboard:global", playerID).Result()
}
// 使用示例
func main() {
   
    addPlayerScore("player_001", 1500)
    addPlayerScore("player_002", 2300)
    addPlayerScore("player_003", 1800)

    rank, _ := getPlayerRank("player_001")
    fmt.Printf("玩家排名:%d\n", rank) // 输出:玩家排名:3

    topPlayers, _ := getTopPlayers(3)
    for i, player := range topPlayers {
   
        fmt.Printf("第%d名:%s - %.0f分\n", i+1, player.Member, player.Score)
    }
}

💡 技巧ZRevRange 按分数降序,ZRange 按分数升序


5️⃣ 消息队列(Message Queue)📨

使用 List 实现轻量级消息队列 [[7]]。

Go 代码实现

// 生产者:入队
func enqueue(queueName string, message interface{
   }) error {
   
    data, _ := json.Marshal(message)
    return rdb.RPush(ctx, fmt.Sprintf("queue:%s", queueName), data).Err()
}

// 消费者:出队(阻塞)
func dequeue(queueName string, timeout time.Duration) ([]byte, error) {
   
    result := rdb.BLPop(ctx, timeout, fmt.Sprintf("queue:%s", queueName))
    if result.Err() != nil {
   
        return nil, result.Err()
    }

    // BLPop 返回 [queueName, message]
    values, _ := result.Result()
    return []byte(values[1]), nil
}

// 消费者:出队(非阻塞)
func dequeueNonBlock(queueName string) ([]byte, error) {
   
    result := rdb.LPop(ctx, fmt.Sprintf("queue:%s", queueName))
    if result.Err() == redis.Nil {
   
        return nil, nil // 队列为空
    }
    return []byte(result.Val()), result.Err()
}

// 获取队列长度
func getQueueLength(queueName string) (int64, error) {
   
    return rdb.LLen(ctx, fmt.Sprintf("queue:%s", queueName)).Result()
}
// 使用示例
go func() {
   
    // 生产者
    for i := 0; i < 10; i++ {
   
        enqueue("tasks", Task{
   ID: i, Data: fmt.Sprintf("task_%d", i)})
    }
}()

go func() {
   
    // 消费者
    for {
   
        data, _ := dequeue("tasks", 5*time.Second)
        if data != nil {
   
            var task Task
            json.Unmarshal(data, &task)
            processTask(task)
        }
    }
}()

6️⃣ 发布订阅(Pub/Sub)📢

实现实时消息推送,聊天室、通知系统必备

Go 代码实现

// 发布者
func publish(channel string, message interface{
   }) error {
   
    data, _ := json.Marshal(message)
    return rdb.Publish(ctx, channel, data).Err()
}

// 订阅者
func subscribe(channels ...string) *redis.PubSub {
   
    pubsub := rdb.Subscribe(ctx, channels...)

    // 确认订阅成功
    _, err := pubsub.Receive(ctx)
    if err != nil {
   
        panic(err)
    }

    return pubsub
}

// 接收消息
func receiveMessage(pubsub *redis.PubSub) (*redis.Message, error) {
   
    msg, err := pubsub.ReceiveMessage(ctx)
    return msg, err
}
// 使用示例
func main() {
   
    // 启动订阅者
    pubsub := subscribe("chat:room1", "chat:room2")
    defer pubsub.Close()

    go func() {
   
        for {
   
            msg, _ := receiveMessage(pubsub)
            fmt.Printf("收到消息 [%s]: %s\n", msg.Channel, msg.Payload)
        }
    }()

    // 发布消息
    publish("chat:room1", Message{
   User: "Alice", Text: "Hello!"})
    publish("chat:room2", Message{
   User: "Bob", Text: "Hi there!"})

    time.Sleep(time.Second)
}

⚠️ 注意:Pub/Sub 消息不持久化,消费者离线会丢失消息


7️⃣ 实时分析(Real-time Analytics)📊

统计页面访问、用户行为等实时数据

Go 代码实现

// 记录页面访问
func recordPageView(pageID, userID string) error {
   
    key := fmt.Sprintf("analytics:page:%s", pageID)

    // 使用 HyperLogLog 统计 UV(去重)
    rdb.PFAdd(ctx, key, userID)

    // 使用 Counter 统计 PV
    rdb.Incr(ctx, fmt.Sprintf("analytics:pv:%s", pageID))

    // 设置过期时间(如 7 天)
    rdb.Expire(ctx, key, 7*24*time.Hour)

    return nil
}

// 获取 UV(独立访客数)
func getUniqueVisitors(pageID string) (int64, error) {
   
    key := fmt.Sprintf("analytics:page:%s", pageID)
    return rdb.PFCount(ctx, key).Result()
}

// 获取 PV(页面访问量)
func getPageViews(pageID string) (int64, error) {
   
    return rdb.Get(ctx, fmt.Sprintf("analytics:pv:%s", pageID)).Int64()
}

// 记录用户在线状态
func setUserOnline(userID string) error {
   
    key := "analytics:online:users"
    return rdb.SAdd(ctx, key, userID).Err()
}

// 获取在线用户数
func getOnlineUserCount() (int64, error) {
   
    return rdb.SCard(ctx, "analytics:online:users").Result()
}

💡 HyperLogLog:基数统计神器,12KB 内存可统计 20 亿独立元素!


8️⃣ 分布式锁(Distributed Lock)🔒

多实例场景下保证资源互斥访问

Go 代码实现

// 获取分布式锁
func acquireLock(lockKey, lockID string, ttl time.Duration) bool {
   
    // SET key value NX EX ttl(原子操作)
    result, err := rdb.Set(ctx, lockKey, lockID, 
        redis.SetArgs{
   
            NX: true,           // 不存在才设置
            EX: int64(ttl.Seconds()),
        }).Result()

    return err == nil && result == "OK"
}

// 释放分布式锁(需要 Lua 脚本保证原子性)
func releaseLock(lockKey, lockID string) bool {
   
    luaScript := `
        if redis.call("get", KEYS[1]) == ARGV[1] then
            return redis.call("del", KEYS[1])
        else
            return 0
        end
    `

    result, _ := rdb.Eval(ctx, luaScript, []string{
   lockKey}, lockID).Int64()
    return result == 1
}

// 使用示例
func processWithLock(resourceID string) error {
   
    lockKey := fmt.Sprintf("lock:resource:%s", resourceID)
    lockID := generateUUID()

    if !acquireLock(lockKey, lockID, 30*time.Second) {
   
        return errors.New("获取锁失败")
    }

    defer releaseLock(lockKey, lockID)

    // 执行临界区代码
    return doCriticalWork()
}

⚠️ 重要:释放锁时必须验证锁 ID,防止误删其他进程的锁!


9️⃣ 地理位置(Geospatial)🌍

存储和查询地理位置数据,O2O 应用必备 [[5]]。

Go 代码实现

type Location struct {
   
    Name      string
    Longitude float64
    Latitude  float64
}

// 添加位置
func addLocation(key string, loc Location) error {
   
    return rdb.GeoAdd(ctx, key, 
        &redis.GeoLocation{
   Name: loc.Name, Longitude: loc.Longitude, Latitude: loc.Latitude},
    ).Err()
}

// 查询附近位置
func findNearby(key string, longitude, latitude float64, radius float64) ([]redis.GeoLocation, error) {
   
    result, err := rdb.GeoRadius(ctx, key, longitude, latitude, 
        &redis.GeoRadiusQuery{
   
            Radius: radius,
            Unit:   "km",
            WithDist: true,
        }).Result()

    if err != nil {
   
        return nil, err
    }

    locations := make([]redis.GeoLocation, len(result))
    for i, r := range result {
   
        locations[i] = r.GeoLocation
    }
    return locations, nil
}

// 计算两地距离
func getDistance(key string, loc1, loc2 string) (float64, error) {
   
    return rdb.GeoDist(ctx, key, loc1, loc2, "km").Result()
}
// 使用示例
func main() {
   
    // 添加城市位置
    addLocation("cities", Location{
   "北京", 116.4074, 39.9042})
    addLocation("cities", Location{
   "上海", 121.4737, 31.2304})
    addLocation("cities", Location{
   "广州", 113.2644, 23.1291})

    // 查找北京附近 1000km 内的城市
    nearby, _ := findNearby("cities", 116.4074, 39.9042, 1000)
    for _, loc := range nearby {
   
        fmt.Printf("%s - 距离:%.2fkm\n", loc.Name, loc.Dist)
    }
}

🔟 购物车(Shopping Cart)🛒

电商场景经典应用,使用 Hash 存储商品信息

Go 代码实现

type CartItem struct {
   
    ProductID string `json:"product_id"`
    Name      string `json:"name"`
    Price     float64 `json:"price"`
    Quantity  int    `json:"quantity"`
}

// 添加商品到购物车
func addToCart(userID, productID string, quantity int) error {
   
    key := fmt.Sprintf("cart:%s", userID)

    // 检查是否已存在
    exists, _ := rdb.HExists(ctx, key, productID).Result()

    if exists {
   
        // 已存在,增加数量
        rdb.HIncrBy(ctx, key, fmt.Sprintf("%s:qty", productID), int64(quantity))
    } else {
   
        // 新商品,存储完整信息
        product := getProduct(productID) // 从数据库获取
        data, _ := json.Marshal(product)
        rdb.HSet(ctx, key, productID, string(data))
        rdb.HSet(ctx, key, fmt.Sprintf("%s:qty", productID), quantity)
    }

    // 设置 30 天过期
    rdb.Expire(ctx, key, 30*24*time.Hour)

    return nil
}

// 获取购物车
func getCart(userID string) ([]CartItem, error) {
   
    key := fmt.Sprintf("cart:%s", userID)

    // 获取所有商品
    data, _ := rdb.HGetAll(ctx, key).Result()

    var items []CartItem
    for productID, jsonData := range data {
   
        if strings.HasSuffix(productID, ":qty") {
   
            continue // 跳过数量字段
        }

        var item CartItem
        json.Unmarshal([]byte(jsonData), &item)

        qtyKey := fmt.Sprintf("%s:qty", productID)
        qty, _ := rdb.HGet(ctx, key, qtyKey).Int64()
        item.Quantity = int(qty)

        items = append(items, item)
    }

    return items, nil
}

// 更新商品数量
func updateCartQuantity(userID, productID string, quantity int) error {
   
    key := fmt.Sprintf("cart:%s", userID)
    return rdb.HSet(ctx, key, fmt.Sprintf("%s:qty", productID), quantity).Err()
}

// 删除商品
func removeFromCart(userID, productID string) error {
   
    key := fmt.Sprintf("cart:%s", userID)
    rdb.HDel(ctx, key, productID, fmt.Sprintf("%s:qty", productID))
    return nil
}

// 清空购物车
func clearCart(userID string) error {
   
    key := fmt.Sprintf("cart:%s", userID)
    return rdb.Del(ctx, key).Err()
}

📊 总结对比

场景 数据结构 核心命令 适用场景
🥇 缓存 String SET/GET + EXPIRE 数据库查询加速
🔐 会话 String/Hash SET/GET + EXPIRE 用户登录状态
🛑 限流 String/Sorted Set INCR/ZADD API 保护
🏆 排行榜 Sorted Set ZADD/ZRANK 游戏/积分排名
📨 消息队列 List RPUSH/BLPOP 异步任务处理
📢 发布订阅 Pub/Sub PUBLISH/SUBSCRIBE 实时通知
📊 实时分析 HyperLogLog/Set PFADD/SADD UV/PV 统计
🔒 分布式锁 String SET NX 资源互斥
🌍 地理位置 Geo GEOADD/GEORADIUS O2O/附近的人
🛒 购物车 Hash HSET/HGET 电商购物车

🎯 最佳实践

// 1. 连接池配置
rdb := redis.NewClient(&redis.Options{
   
    Addr:         "localhost:6379",
    PoolSize:     100,              // 连接池大小
    MinIdleConns: 10,               // 最小空闲连接
    MaxRetries:   3,                // 最大重试次数
    PoolTimeout:  30 * time.Second, // 连接超时
})

// 2. 健康检查
func checkRedisHealth() bool {
   
    _, err := rdb.Ping(ctx).Result()
    return err == nil
}

// 3. 优雅关闭
defer rdb.Close()

相关文章
|
6天前
|
人工智能 安全 API
CoPaw:5分钟部署你的 AI助理
源自阿里巴巴开源生态的个人 AI 助理——CoPaw。作为阿里倾力打造的开源力作,CoPaw 完美打通钉钉、飞书、Discord 等多平台对话通道,支持定时任务自动化。内置 PDF/Office 深度处理、新闻摘要等强大技能,更开放自定义扩展接口。坚持数据全程私有化部署,绝不上传云端,让每一位用户都能在大厂技术加持下,拥有安全、专属的智能助手。
|
9天前
|
人工智能 自然语言处理 JavaScript
2026年Windows+Ollama本地部署OpenClaw保姆级教程:本地AI Agent+阿里云上快速搭建
2026年OpenClaw凭借本地部署、私有化运行的特性,成为打造个人智能体的核心工具,而Ollama作为轻量级本地大模型管理工具,能让OpenClaw摆脱对云端大模型的依赖,实现**本地推理、数据不泄露、全流程私有化**的智能体验。本文基于Windows 11系统,从硬件环境准备、Ollama安装与模型定制、OpenClaw部署配置、技能扩展到常见问题排查,打造保姆级本地部署教程,同时补充阿里云OpenClaw(Clawdbot)快速部署步骤,兼顾本地私有化需求与云端7×24小时运行需求,文中所有代码命令均可直接复制执行,确保零基础用户也能快速搭建属于自己的本地智能体。
10363 80
|
7天前
|
人工智能 安全 JavaScript
阿里云上+本地部署OpenClaw(小龙虾)新手攻略:解锁10大必备Skills,零基础也能玩转AI助手
2026年,开源AI代理工具OpenClaw(昵称“小龙虾”)凭借“能实际做事”的核心优势,在GitHub斩获25万+星标,成为现象级AI工具。它最强大的魅力在于可扩展的Skills(技能包)系统——通过ClawHub插件市场的数百个技能,能让AI助手从简单聊天升级为处理办公、学习、日常事务的全能帮手。
6012 16
|
8天前
|
人工智能 自然语言处理 机器人
保姆级教程:Mac本地搭建OpenClaw及阿里云上1分钟部署OpenClaw+飞书集成实战指南
OpenClaw(曾用名Clawdbot、Moltbot)作为2026年最热门的开源个人AI助手平台,以“自然语言驱动自动化”为核心,支持对接飞书、Telegram等主流通讯工具,可替代人工完成文件操作、日历管理、邮件处理等重复性工作。其模块化架构适配多系统环境,既可以在Mac上本地化部署打造私人助手,也能通过阿里云实现7×24小时稳定运行,完美兼顾隐私性与便捷性。
5917 13
|
10天前
|
人工智能 JSON JavaScript
手把手教你用 OpenClaw + 飞书,打造专属 AI 机器人
手把手教你用 OpenClaw(v2026.2.22-2)+ 飞书,10分钟零代码搭建专属AI机器人!内置飞书插件,无需额外安装;支持Claude等主流模型,命令行一键配置。告别复杂开发,像聊同事一样自然对话。
6118 15
手把手教你用 OpenClaw + 飞书,打造专属 AI 机器人
|
5天前
|
人工智能 JavaScript Ubuntu
5分钟上手龙虾AI!OpenClaw部署(阿里云+本地)+ 免费多模型配置保姆级教程(MiniMax、Claude、阿里云百炼)
OpenClaw(昵称“龙虾AI”)作为2026年热门的开源个人AI助手,由PSPDFKit创始人Peter Steinberger开发,核心优势在于“真正执行任务”——不仅能聊天互动,还能自动处理邮件、管理日程、订机票、写代码等,且所有数据本地处理,隐私完全可控。它支持接入MiniMax、Claude、GPT等多类大模型,兼容微信、Telegram、飞书等主流聊天工具,搭配100+可扩展技能,成为兼顾实用性与隐私性的AI工具首选。
3541 7
|
2天前
|
人工智能 JavaScript 测试技术
保姆级教程:OpenClaw阿里云及本地部署+Claude Code集成,打造全能 AI 编程助手
在AI编程工具百花齐放的2026年,Anthropic推出的Claude Code凭借72.5%的SWE-bench测试高分、25倍于GitHub Copilot的上下文窗口,成为开发者追捧的智能编程助手。但单一工具仍有局限——Claude Code擅长代码生成与审查,却缺乏灵活的部署与自动化执行能力;而OpenClaw(前身为Clawdbot)作为开源AI代理框架,能完美弥补这一短板,通过云端与本地双部署,实现“代码开发-测试-部署”全流程自动化。
1747 13
|
5天前
|
人工智能 JavaScript API
阿里云及本地 Windows 部署(OpenClaw+Ollama)保姆级教程及技能扩展与问题排查
OpenClaw(原Clawdbot)作为2026年主流的开源AI智能体工具,具备系统级操作权限,能将自然语言指令转化为文件操作、程序控制等实际行为。搭配轻量级本地大模型管理工具Ollama,可实现本地推理、数据私有化存储的全闭环;而阿里云提供的云端部署方案,则能满足7×24小时稳定运行需求。本文将详细拆解2026年阿里云与本地(Windows 11系统)部署OpenClaw的完整流程,包含Ollama模型定制、技能扩展及常见问题排查,所有代码命令可直接复制执行,零基础用户也能快速上手。
2149 3