当前位置:首页 > 文章列表 > 数据库 > Redis > Redis缓存旁路控制数据陈旧时间

Redis缓存旁路控制数据陈旧时间

来源:17golang原创 2026-11-15 11:31:38 0浏览 收藏

Redis 缓存旁路最容易被忽略的不是“要不要加缓存”,而是数据最多允许旧多久。把这个时间直接换成缓存键的 TTL,再配合写入后的主动失效,通常比试图让 Redis 和数据库时时同步更稳。本文用一个商品详情读取场景,把正值、空值和热点键分别放进可解释的边界里。

官方地址:https://redis.io/docs/latest/develop/use-cases/cache-aside/

TTL 是允许缓存继续服务旧值的上限,不是数据一定会在这个时刻被刷新。允许短暂陈旧的读场景可以依赖 TTL;不能接受旧值的写场景,应在权威数据更新成功后删除对应缓存键。

先把陈旧时间换算成 Redis TTL

假设商品名称允许最多旧 60 秒,价格只允许旧 5 秒。那就不要给所有实体统一设置 30 分钟 TTL,而应让业务字段的陈旧容忍度决定缓存策略。TTL 越短,读命中率可能越低、回源压力越高;TTL 越长,命中率更好,但旧值窗口也更大。

Redis 的过期时间可以用秒或毫秒精度设置,常用的 SET ... EX、SET ... PX 或单独的 EXPIRE 都是在给键设置时间边界。对缓存旁路来说,关键不是记住某个命令,而是让“业务容忍度”和“缓存键 TTL”能够一一对应。

Redis缓存旁路中业务陈旧容忍度、TTL配置、缓存键和权威数据库的静态关系说明图
图1:Redis 缓存旁路的 TTL 关系说明图,展示陈旧窗口与缓存键边界,不是运行截图。

读路径先查缓存,未命中再回源

读旁路的最小实现是:先读 Redis,命中就返回;未命中才查询数据库或其他权威服务,然后把结果带 TTL 写回。下面的 Go 片段刻意把 TTL 作为参数传入,避免在业务方法里藏一个无法解释的常量。

// GetProduct 展示缓存旁路:Redis 命中直接返回,未命中才访问权威源。
func (c *Cache) GetProduct(ctx context.Context, id string, ttl time.Duration) (Product, error) {
    key := "cache:product:" + id
    cached, err := c.rdb.Get(ctx, key).Result()
    if err == nil {
        // 命中后只做反序列化,不再访问数据库。
        var product Product
        if json.Unmarshal([]byte(cached), &product) == nil {
            return product, nil
        }
    }
    if err != nil && !errors.Is(err, redis.Nil) {
        return Product{}, err
    }

    // 数据库是权威源;真实项目中这里还应记录回源次数。
    product, err := c.repo.FindProduct(ctx, id)
    if err != nil {
        return Product{}, err
    }
    payload, err := json.Marshal(product)
    if err != nil {
        return Product{}, err
    }
    // EX 由客户端用 ttl 表达,保证每个缓存键都有明确过期边界。
    if err := c.rdb.Set(ctx, key, payload, ttl).Err(); err != nil {
        return Product{}, err
    }
    return product, nil
}

这里的 redis.Nil 代表键不存在,不应和 Redis 连接错误混为一谈。缓存写入失败时是否继续返回数据库结果,要由接口的可用性目标决定;如果缓存只是加速层,通常可以记录错误后仍返回权威源结果。

写入成功后删除键,不要手工“同步”旧对象

更新商品时,先提交数据库,再删除 cache:product:{id}。下一次读取自然会重新加载最新值。这样做的好处是逻辑简单:缓存不承担权威性,也不会因为更新顺序或部分字段遗漏而长期保存一份看似完整、实际过期的对象。

// UpdateProduct 只在权威写入成功后删除缓存,避免提前清除旧值。
func (c *Cache) UpdateProduct(ctx context.Context, product Product) error {
    if err := c.repo.UpdateProduct(ctx, product); err != nil {
        return err
    }
    // DEL 让下一次读回到权威源,避免继续服务旧对象。
    return c.rdb.Del(ctx, "cache:product:"+product.ID).Err()
}

如果业务允许极短的旧值窗口,也可以使用 TTL 作为兜底;但对于价格、库存、权限这类写入后必须尽快可见的数据,主动删除比等待 TTL 到期更直接。

空值和热点键要单独设边界

查询不存在的商品时,如果每次请求都直达数据库,攻击性探测或输入错误会反复击穿同一个 ID。可以缓存一个带短 TTL 的“确实不存在”标记,但空值 TTL 应明显短于正值 TTL,以便新建记录尽快可见。

另一类问题是热点键同时过期。大量请求会在同一时刻发现缓存为空,如果每个请求都回源,就会把数据库瞬间推高。可用单飞锁让一个请求负责回源,其余请求短暂等待缓存重新出现;锁的 TTL 要长于最坏的权威查询耗时,并用唯一 token 防止过期后误删别人的锁。

Redis热点键、单飞锁、权威查询、正值TTL和空值TTL的静态关系说明图
图2:热点键与空值缓存边界说明图,展示单飞锁和两种 TTL 的职责,不是运行截图。
# 查看缓存内容与剩余 TTL;这里只做排查,不把命令输出当成业务真相。
redis-cli HGETALL cache:product:p-001
redis-cli TTL cache:product:p-001

# 写入一个带 30 秒过期时间的字符串缓存,EX 的单位是秒。
redis-cli SET cache:product:p-001 '{"id":"p-001"}' EX 30

用 TTL、命中率和回源次数复查策略

上线后至少区分三类指标:缓存命中率、权威源回读次数、热点键失效后的并发等待次数。单看命中率容易误判:命中率高但 TTL 过长,可能只是把旧值服务得更久;TTL 很短却没有回源告警,也可能是流量很低。

可以抽样读取 TTL 或 PTTL 观察剩余时间,结合业务的陈旧投诉和数据库压力调整。最后用这张速查表收口:

场景建议边界
普通读多写少正值缓存 + TTLTTL 不超过业务可接受陈旧窗口
写入必须尽快可见权威写成功后 DEL删除失败要记录并处理重试
不存在的 ID 被反复查询空值短缓存空值 TTL 要短于正值 TTL
热点键同时失效单飞锁或提前刷新锁 TTL 覆盖最坏回源耗时

相关问题

TTL 到期后一定会立刻删除键吗?不要把它当作精确的业务事件;TTL 的职责是限定可服务窗口,关键写入仍应主动失效。

为什么不直接把数据库更新后的对象 SET 回 Redis?删除后按需回填的状态更少,能减少部分更新、顺序竞争和缓存结构漂移。

空值要不要缓存?只有当不存在查询确实会重复打到权威源时才缓存,并给它更短的 TTL。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Redis令牌桶实现分布式限流Redis令牌桶实现分布式限流
上一篇
Redis令牌桶实现分布式限流
Redis队列处理失败任务可见性超时
下一篇
Redis队列处理失败任务可见性超时
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    1036次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    1034次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1178次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1139次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    981次使用