栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

缓存一致性设计方案

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

缓存一致性设计方案

文章目录
  • 前言
  • 1. 缓存一致性
  • 2. 缓存一致性设计分析
      • 2.1 先淘汰缓存,再更新数据
      • 2.2 先淘汰缓存,再更新数据
  • 3. 实用方案-延时二次淘汰

前言

公司项目中发生了缓存一致性的问题,具体场景如下:

  1. 系统的业务配置更新为生效状态后,业务服务实际使用的配置数据还是旧版本的数据
  2. 偶现业务服务使用的配置数据不完整,造成业务流程异常

项目中引入了缓存,排查问题时发现 redis 中缓存的数据就是旧版本数据,而且存在缓存中配置数据不完整的现象。分析代码,发现导致缓存数据与数据库数据不一致的原因有两个,本文主要分析解决 缓存淘汰设计不合理 的问题

  1. MySQL读写分离
    项目中使用了公司的读写分离框架,查询数据时默认读从库。因为主从延迟 的存在,主库数据更新后需要一定时间才能在从库读到更新后的数据,如果在这期间缓存已经失效并且恰好有查询进来,只能查询到从库旧数据,并且会将其缓存到 redis。这个点比较好解决,只要指定查询主库就可以
  2. 缓存淘汰设计不合理
    项目代码中将淘汰缓存的操作包裹在 @Transcational 注解的方法的末尾,这样做实际是在事务提交前就淘汰了缓存,当并发较高时,如果查询在更新事务执行时进行,并且持续到更新事务结束,这样未加入同一个事务的多表查询就可能存在有些表数据能查到,有些表数据在更新事务结束后查不到的情况,也就造成了缓存中配置数据的不完整
1. 缓存一致性

应用系统通常会引入缓存来缓解数据库压力,这样可以提高系统吞吐,提升用户体验,但也带来了数据污染的风险,也就是缓存一致性问题。所谓缓存一致性指的是缓存数据和数据库数据之间的一致,如果没有合理的缓存设计,很容易导致缓存与数据库之间的不一致问题

2. 缓存一致性设计分析

缓存穿透等概念 中笔者大致介绍了缓存相关概念,缓存一致性问题总是由数据库源数据更新引发,所以在数据更新时首先要确定缓存的处理方式,通常的处理策略是淘汰缓存而不是更新缓存,原因如下:

  1. 更新数据库时如果同步更新缓存数据,由于二者一般属于不同的中间件,很难保证原子操作,则必然存在并发更新缓存造成的数据不一致
  2. 更新缓存数据通常需要序列化,如果将其和数据库更新的操作绑定在一起,会额外提高数据更新操作的性能成本

基于以上共识,现在要考虑的就是在更新数据时如何淘汰缓存,直观的处理方式有两种,但是二者都有一定的问题,下文将详细说明

  1. 先淘汰缓存,再更新数据
  2. 先更新数据,再淘汰缓存
2.1 先淘汰缓存,再更新数据

这个方案可能导致问题的处理流程如下图所示,示意图中有两个线程在操作缓存,具体步骤如下:

  1. 线程1 进行数据库更新操作,在数据库更新之前首先淘汰掉缓存
  2. 线程1 开启更新事务,执行数据库更新
  3. 在 线程1 执行更新事务时,线程2 执行数据查询,首先去查询缓存
  4. 由于 线程1 已经淘汰掉缓存,此时线程2 未查询到缓存,只能去查询数据库
  5. 线程1 的更新事务执行耗时比较长,线程2 查询数据库只能查到旧的数据,并在查到旧数据后将其缓存起来
  6. 线程1 更新事务执行完毕,更新后的最新数据对外可见,但是缓存中已经被线程2保存了旧数据
  7. 由于以上原因,数据库数据与缓存数据出现不一致

2.2 先淘汰缓存,再更新数据

这个方案的出现异常的流程如下:

  1. 线程1 进行数据库更新操作,开启更新事务
  2. 线程2 执行数据查询,此时缓存中数据不存在或者已经过期淘汰,只能去查数据库
  3. 线程2 查询数据库,此时只能查到旧数据,但是由于网络波动、GC 等原因,一直没有拿到数据库返回的数据
  4. 在 线程2 等待数据库返回数据期间,线程1 更新事务执行提交,数据库最新数据对外可见
  5. 线程1 更新事务执行完毕,淘汰掉旧的缓存数据
  6. 线程2 在 线程1 淘汰缓存之后终于拿到了数据库返回的旧数据,并使用旧数据去更新缓存
  7. 由于以上原因,数据库数据与缓存数据出现不一致

3. 实用方案-延时二次淘汰

针对上一节提到的问题,一个能较大限度降低缓存一致性风险的方案是进行缓存二次淘汰,其处理流程如下:

  1. 线程1 进行数据库更新操作,在数据库更新之前首先淘汰掉缓存
  2. 线程1 开启更新事务,执行数据库更新
  3. 在 线程1 执行更新事务时,线程2 执行数据查询,首先去查询缓存
  4. 由于 线程1 已经淘汰掉缓存,此时线程2 未查询到缓存,只能去查询数据库,此时查到旧数据,但是由于网络波动、GC 等原因,一直没有拿到数据库返回的数据
  5. 线程1 的更新事务终于提交,数据库最新数据对外可见
  6. 线程1 更新事务执行完毕,生成延时任务用于淘汰缓存
  7. 线程2 此时终于拿到了数据库返回的旧数据,并使用旧数据去更新缓存
  8. 由于以上原因,数据库数据与缓存数据出现短时不一致
  9. 线程1提交的延时任务执行,淘汰掉 线程2 更新的缓存数据,缓存中没有数据,下次查询必然要走数据库,则消除了缓存不一致的问题

需注意,延时任务的延时时间需要大于读取缓存的平均耗时(也即是线程2读取缓存,更新缓存这个过程的耗时)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/333227.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号