缓存更新策略

缓存更新策略


一、前言

在真实业务中,数据库的数据会频繁发生变化,而缓存中的数据则是数据库数据的副本。如何保证缓存与数据库的数据一致性,是缓存使用中必须解决的核心问题。

常见的缓存更新策略可分为三大类:

  • 内存淘汰:Redis 自带的内存淘汰机制,当内存达到 maxmemory 上限时,自动按规则淘汰部分 key,是一种被动的、无需人工参与的手段。

  • 超时剔除:给缓存设置过期时间(TTL),到期自动删除。这是最简单有效的最终一致性保证,任何缓存数据都应该设置合理的过期时间。

  • 主动更新:当数据库数据发生变化时,由开发者编码主动更新或删除缓存。这是保证高一致性场景下数据同步的核心手段。

不同的业务场景对一致性的要求不同:

  • 高一致性:如金融、交易、库存扣减等,缓存与数据库必须保持近乎实时一致。

  • 低一致性:如文章阅读量、点赞数、商品评价等,可以接受短暂的不一致,甚至允许一定程度的丢失。

二、主动更新策略

主动更新主要有三种实现模式:

  • 由缓存的调用者,在更新数据库的同时更新缓存(可控性最高,也是最常用的方式)

  • 缓存与数据库整合为一个独立的服务,由该服务内部维护一致性,调用者无需关心缓存同步细节(如 Tair、Aerospike 等部分特性)。

  • 调用者只操作缓存,由另一个异步线程负责将缓存数据持久化到数据库,保证最终一致(如先写 Redis 再异步落库)。

异步更新的好处:可以合并多次写操作,减轻数据库压力。
异步更新的坏处:存在数据丢失风险(如进程崩溃导致缓冲区内数据未落库);实现复杂度高,需要处理消息顺序、幂等、重试等一系列分布式问题。

三、操作缓存和数据库需要解决的三个问题

在实现"更新数据库 + 更新缓存"这一主动策略时,必然面临三个关键选择。

1.删除缓存还是更新缓存?

删除缓存优于更新缓存

  • 更新缓存:每次更新数据库都更新缓存,无效写操作较多
  • 删除缓存:更新数据库时让缓存失效,查询时再更新缓存

更新缓存操作的缺点:大量无效写------例如一个热点数据 1 分钟内更新了 100 次,但被查询可能只有 2 次,其余 98 次缓存更新都是浪费资源;若缓存结构复杂(如多表关联),每次重新构建成本也很高。

2.如何保证缓存与数据库操作的同时成功或失败?

  • 单体系统:将缓存与数据库操作放在一个事务。若缓存删除失败,回滚数据库事务;或者利用 Spring 的 @Transactional + 缓存切面保证原子性。
  • 分布式系统:可利用 TCC、Seata 等分布式事务框架,或者采用最终一致性方案:数据库更新后,发送 MQ 消息,由消费者异步删除缓存,利用消息队列的重试机制保证缓存删除成功

3.先操作缓存还是先操作数据库?

先操作数据库,再删除缓存更好

  • 先删除缓存,再操作数据库:线程1 删除缓存成功,线程2 查询未命中缓存,读数据库得到旧数据,线程2 将读到的旧数据写入缓存,线程1 更新数据库完毕。
    • 这样导致缓存中永远是旧数据,直到缓存过期或下次更新才会被修正
  • 先操作数据库,再删除缓存:线程1来查询是缓存刚好失效,线程1 从缓存查询未命中,读数据库得到旧数据,线程2 更新数据库成功,删除缓存,最后线程1 将旧数据写入缓存。
    • 同样会导致缓存中短暂存在旧数据,直到下一次更新或缓存过期

先操作数据库,再删除缓存发生异常的情况概率要小一些,要满足线程1来查询时恰好缓存失效,线程1写缓存的操作要在另一个线程执行完毕之后,但是写缓存的操作大多是微秒级别,速度很快,而数据库的更新往往是毫秒级甚至更长。这种时间窗口极窄。

对于这种问题,可以通过给缓存设置一个合理的超时时间(TTL) 来解决。这个超时时间设置在缓存数据上(例如 SET user:1 value EX 1800),即使出现了脏数据,最多存活 30 分钟就会被自动清除,保证最终一致性。

四、缓存更新的最佳实践

采用主动更新数据库和缓存的方案,由缓存的调用者,在更新数据库的同时更新缓存。

对于读操作:

  • 缓存命中 → 直接返回。
  • 缓存未命中 → 查询数据库,将结果写入缓存,并设定合理的超时时间(如 30~60 分钟,根据业务容忍度调整)。

对于写操作:

  • 先写数据库(更新/删除/插入)。
  • 再删除对应的缓存(注意缓存 key 的粒度,确保删干净)。
  • 若对一致性要求极高,可将数据库操作与缓存删除放入同一事务;若删缓存失败,考虑通过 MQ 异步重试。
相关推荐
AI进化营-智能译站2 小时前
ROS2 C++开发系列08-传感器数据缓存与指令解析方式之数组、向量与字符串实战
开发语言·c++·缓存·ai
许彰午2 小时前
CacheSQL(一):手写数据库的工程化重生
java·数据库·缓存
aXin_ya3 小时前
微服务第九天 分布式缓存(Redis)
分布式·缓存·微服务
代码飞天3 小时前
CTF之内存取证——瞬息万变成为一瞬
安全·web安全·缓存
许彰午4 小时前
CacheSQL(四):CacheSQLClient——用一张路由表实现水平扩展
java·数据库·缓存·系统架构·政务
Lyyaoo.4 小时前
缓存穿透/雪崩/击穿
数据库·缓存·oracle
许彰午4 小时前
CacheSQL(三):双 HTTP 引擎与 SQL 查询——接口抽象的价值
java·数据库·sql·缓存
Flying pigs~~13 小时前
RAG智慧问答项目
数据库·人工智能·缓存·微调·知识库·rag
许彰午14 小时前
CacheSQL(二):主从复制——OpLog 环形缓冲区与故障自动恢复
java·数据库·缓存