java缓存与数据库如何保证高一致性,怎么保证缓存一致性

  java缓存与数据库如何保证高一致性,怎么保证缓存一致性

  00-1010前言:方案分析方案一:更新缓存、更新数据库方案二:更新数据库方案三:删除缓存、更新数据库方案四:更新数据库、比较总结推荐方案延迟双删除实际场景写缓存策略读缓存策略注

  

目录

一道之前的面试题:

 

  如何保证缓存和数据库的一致性?

  下面是几个方案(回答时最好根据自己的业务结合以下方案)

  

前言:

更新缓存策略方式常见的有下面几种:

 

  先更新缓存,再更新数据库,再更新缓存,再删除缓存,再更新数据库,再删除缓存。下面介绍一下!

  00-1010这种方法很容易排除,因为如果先更新缓存成功,但数据库更新不成功,肯定会造成数据不一致。

  00-1010这种缓存更新策略俗称双写,但是有一个问题是数据库并发更新时脏数据会被刷到缓存中。

  updateDB();updateRedis();例如,如果数据库和缓存在两个操作之间被后来的请求修改,此时更新缓存已经是过时的数据。

  00-1010在存在问题:,更新数据库之前如果有查询请求,脏数据会被刷入缓存。

  delete redis();updateDB();如果举例:在两个操作之间有数据查询,旧数据将被放入缓存。

  这种方案将导致请求数据不一致。

  同时,如果有一个更新操作的请求A和另一个查询操作的请求B。那么就会出现下面的情况3360。

  a请求写入和删除缓存。b请求查询,发现缓存不存在。请求查询数据库以获取旧值。请求将旧值写入缓存。a请求将新值写入数据库。以上情况会导致不一致。此外,如果不采用为缓存设置过期时间的策略,数据将始终是脏的。

  00-1010存在问题:在更新数据库前有查询请求,缓存无效。它将查询数据库,然后更新缓存。如果在查询数据库和更新缓存之间更新了数据库,脏数据将被刷入缓存。

  updateDB();delete redis();如果举例:在查询数据库并将其放入缓存的过程中更新数据,并删除缓存,旧数据将被放入缓存。

  假设有两个请求,一个请求A用于查询操作,另一个请求B用于更新操作,那么将会出现以下情况

  就在缓存出现故障时,请求A查询数据库并获得一个旧值请求B将新值写入数据库请求B删除缓存请求A将找到的旧值写入缓存。如果出现这种情况,确实会出现脏数据。但是上述情况有一个先天条件,就是写数据库的操作比读数据库的操作花费的时间少。

  不过数据库的读操作的速度远快于写操作的

  所以这种情况很难出现。

  

方案分析

方案1和方案2的共同缺点:

 

  并发更新数据库时,脏数据会被刷入缓存,但并发写入的概率相对较小。

  从线程安全的角度来看,会产生脏数据,比如:

  线程A更新了数据库线程B更新了数据库线程B更新了缓存线程A更新了缓存方案3和方案4的共同缺点:

  无论采用哪种顺序,两种方式都存在一些问题:

  主从延迟问题:无论是先删除还是后删除,数据库的主从延迟都可能导致脏数据。无法删除缓存:如果缓存删除失败,将会生成脏数据。解决问题的方法:延迟双重删除,增加重试机制,下面介绍!

  更新缓存还是删除缓存?

  1.更新缓存需要一定的维护成本,还会有并发更新的问题。2.写的多读的少的时候,读请求还没来,缓存已经更新很多次了,起不到缓存的作用。3.可以在复杂的计算之后计算放入高速缓存的值。如果每次都更新写入缓存的值,那么浪费性能的删除缓存优点:简单,成本低,易于开发。缺点:会造成缓存缺失。

  如果缓存被更新

  开销较小并且读多写少,基本不会有写并发的时候可以才用更新缓存,否则通用做法还是删除缓存。

  

 

  

总结

方案问题问题出现概率推荐程度更新缓存 -> 更新数据库为了保证数据准确性,数据必须以数据库更新结果为准,所以该方案绝不可行大不推荐更新数据库 -> 更新缓存并发更新数据库场景下,会将脏数据刷到缓存并发写场景,概率一般写请求较多时会出现不一致问题,不推荐使用。删除缓存 -> 更新数据库更新数据库之前,若有查询请求,会将脏数据刷到缓存并发读场景,概率较大读请求较多时会出现不一致问题,不推荐使用更新数据库 -> 删除缓存在更新数据库之前有查询请求,并且缓存失效了,会查询数据库,然后更新缓存。如果在查询数据库和更新缓存之间进行了数据库更新的操作,那么就会把脏数据刷到缓存并发读场景&读操作慢于写操作,概率最小读操作比写操作更慢的情况较少,相比于其他方式出错的概率小一些。勉强推荐。

 

  

推荐方案

 

  

延迟双删

采用更新前后双删除缓存策略

 

  

public void write(String key,Object data){ redis.del(key); db.update(data); Thread.sleep(1000); redis.del(key); }

先淘汰缓存再写数据库休眠1秒,再次淘汰缓存大家应该评估自己的项目的读数据业务逻辑的耗时。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上即可。

 

  这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。

  问题及解法:

  1、同步删除,吞吐量降低如何处理

  将第二次删除作为异步的,提交一个延迟的执行任务

  2、解决删除失败的方式:

  添加重试机制,例如:将删除失败的key,写入消息队列;但对业务耦合有些严重;

  

 

  延时工具可以选择:

  最普通的阻塞Thread.currentThread().sleep(1000);

  Jdk调度线程池,quartz定时任务,利用jdk自带的delayQueue,netty的HashWheelTimer,Rabbitmq的延时队列,等等

  

 

  

实际场景

我们有个商品中心的场景,是读多写少的服务,并且写数据会发送MQ通知下游拿数据,这样就需要严格保证缓存和数据库的一致性,需要提供高可靠的系统服务能力。

 

  

 

  

写缓存策略

缓存key设置失效时间先DB操作,再缓存失效写操作都标记key(美团中间件)强制走主库接入美团中间件监听binlog(美团中间件)变化的数据在进行兜底,再删除缓存

 

  

 

  

读缓存策略

先判断是否走主库如果走主库,则使用标记(美团中间件)查主库如果不是,则查看缓存中是否有数据缓存中有数据,则使用缓存数据作为结果如果没有,则查DB数据,再写数据到缓存

 

  

 

  

注意

关于缓存过期时间的问题

 

  如果缓存设置了过期时间,那么上述的所有不一致情况都只是暂时的。

  但是如果没有设置过期时间,那么不一致问题就只能等到下次更新数据时解决。

  所以一定要设置缓存过期时间

  到此这篇关于Java缓存一致性问题的文章就介绍到这了,更多相关Java缓存一致性内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

相关文章阅读

  • 关系型数据库与非关系型数据库简介一样吗,关系型数据库非关系型数据库有哪些
  • 关系型数据库与非关系型数据库简介一样吗,关系型数据库非关系型数据库有哪些,关系型数据库与非关系型数据库简介
  • 关于redis数据库入门详细介绍图片,redis数据库的使用,关于Redis数据库入门详细介绍
  • 使用php连接mysql数据库,php连接数据库的方法
  • 使用php连接mysql数据库,php连接数据库的方法,一文详解PHP连接MySQL数据库的三种方式
  • 什么是分库分表,为什么要进行分库分表-,分库分表的区别,数据库分库分表是什么,什么情况下需要用分库分表
  • vb中adodb连接数据库,
  • treeview控件绑定数据,wpf treeview数据绑定,详解TreeView绑定数据库
  • sql的多表查询,数据库如何实现多表查询
  • SQL数据库的图形管理界面工具是,sql图形界面创建数据库
  • SQL数据库的图形管理界面工具是,sql图形界面创建数据库,SQLServer2019 数据库的基本使用之图形化界面操作的实现
  • sql数据库定时备份怎么弄,mysql 定期备份
  • sql数据库定时备份怎么弄,mysql 定期备份,MySQL 数据库定时备份的几种方式(全面)
  • sqlserver的nvarchar和varchar,数据库varchar和nvarchar
  • sqlserver的nvarchar和varchar,数据库varchar和nvarchar,SQL中varchar和nvarchar的基本介绍及其区别
  • 留言与评论(共有 条评论)
       
    验证码: