精华内容
下载资源
问答
  • 比较复杂的数据不一致问题... 完了,数据库和缓存中的数据不一样了。。。。   只有在对一个数据在并发的进行读写的时候,才可能会出现这种问题 其实如果说你的并发量很低的话,特别是读并发很低,每天访问量就1...

    比较复杂的数据不一致问题描述


    数据发生了变更,先删除了缓存,然后要去修改数据库,此时还没修改

    一个请求过来,去读缓存,发现缓存空了,去查询数据库,查到了修改前的旧数据,放到了缓存中

    数据变更的程序完成了数据库的修改 。 完了,数据库和缓存中的数据不一样了。。。。

     

    只有在对一个数据在并发的进行读写的时候,才可能会出现这种问题 其实如果说你的并发量很低的话,特别是读并发很低,每天访问量就1万次,那么很少的情况下,会出现刚才描述的那种不一致的场景

    但是问题是,如果每天的是上亿的流量,每秒并发读是几万,每秒只要有数据更新的请求,就可能会出现上述的数据库+缓存不一致的情况

    高并发了以后,问题是很多的

    方案 : 数据库与缓存更新与读取操作进行异步串行化 

    更新数据的时候,根据数据的唯一标识,将操作路由之后,发送到一个jvm内部的队列中

    读取数据的时候,如果发现数据不在缓存中,那么将重新读取数据+更新缓存的操作,

    根据唯一标识路由之后,也发送同一个jvm内部的队列中 一个队列对应一个工作线程 ;

    每个工作线程串行拿到对应的操作,然后一条一条的执行

    这样的话,一个数据变更的操作,先执行,删除缓存,然后再去更新数据库,但是还没完成更新

    此时如果一个读请求过来,读到了空的缓存,那么可以先将缓存更新的请求发送到队列中,此时会在队列中积压,然后同步等待缓存更新完成 这里有一个优化点,一个队列中,其实多个更新缓存请求串在一起是没意义的,因此可以做过滤,如果发现队列中已经有一个更新缓存的请求了,那么就不用再放个更新请求操作进去了,直接等待前面的更新操作请求完成即可 

    高并发的场景下,该解决方案要注意的问题

    由于读请求进行了非常轻度的异步化,所以一定要注意读超时的问题,每个读请求必须在超时时间范围内返回

    该解决方案,最大的风险点在于说,可能数据更新很频繁,导致队列中积压了大量更新操作在里面,然后读请求会发生大量的超时,最后导致大量的请求直接走数据库。

    针对读高并发,读缓存架构的项目,一般写请求相对读来说,是非常非常少的,每秒的QPS能到几百就不错了
    一秒,500的写操作,5份,每200ms,就100个写操作
    单机器,20个内存队列,每个内存队列,可能就积压5个写操作,每个写操作性能测试后,一般在20ms左右就完成

    那么针对每个内存队列中的数据的读请求,也就最多hang一会儿,200ms以内肯定能返回了  

    写QPS扩大10倍,但是经过刚才的测算,就知道,单机支撑写QPS几百没问题,那么就扩容机器,扩容10倍的机器,
    10台机器,每个机器20个队列,200个队列

    大部分的情况下,应该是这样的,大量的读请求过来,都是直接走缓存取到数据的

    读请求并发量过高
    这里还必须做好压力测试,确保恰巧碰上上述情况的时候,还有一个风险,就是突然间大量读请求会在几十毫秒的延时hang在服务上,
    看服务能不能抗的住,需要多少机器才能抗住最大的极限情况的峰值
    但是因为并不是所有的数据都在同一时间更新,缓存也不会同一时间失效,所以每次可能也就是少数数据的缓存失效了
    ,然后那些数据对应的读请求过来,并发量应该也不会特别大
    按1:99的比例计算读和写的请求,每秒5万的读QPS,可能只有500次更新操作
    如果一秒有500的写QPS,那么要测算好,可能写操作影响的数据有500条,这500条数据在缓存中失效后,可能导致多少读请求,发送读请求到库存服务来,要求更新缓存
    一般来说,1:1,1:2,1:3,每秒钟有1000个读请求,会hang在库存服务上,每个读请求最多hang多少时间,200ms就会返回
    在同一时间最多hang住的可能也就是单机200个读请求,同时hang住
    单机hang200个读请求,还是ok的
    1:20,每秒更新500条数据,这500秒数据对应的读请求,会有20 * 500 = 1万

    1万个读请求全部hang在库存服务上,就死定了

     

    多服务实例部署的请求路由

     


    可能这个服务部署了多个实例,那么必须保证说,执行数据更新操作,以及执行缓存更新操作的请求,
    都通过nginx服务器路由到相同的服务实例上

     

     

     

     

    热点商品的路由问题,导致请求的倾斜

     


    万一某个商品的读写请求特别高,全部打到相同的机器的相同的队列里面去了,可能造成某台机器的压力过大


    就是说,因为只有在商品数据更新的时候才会清空缓存,然后才会导致读写并发,所以更新频率不是太高的话,
    这个问题的影响并不是特别大


    但是的确可能某些机器的负载会高一些

     

     

     

    展开全文
  • 不过,当缓存的读写模式不同时,缓存数据不一致的发生情况不一样,我们的应对方法也会有所不同,所以,我们先按照缓存读写模式,来分别了解下不同模式下的缓存不一致情况。我在第 23 讲中讲过,根据是否接收写请求,...

    缓存和数据库的数据不一致是如何发生的?
    首先,我们得清楚“数据的一致性”具体是啥意思。其实,这里的“一致性”包含了两种情况:

    • 缓存中有数据,那么,缓存的数据值需要和数据库中的值相同;
    • 缓存中本身没有数据,那么,数据库中的值必须是最新值。

    不符合这两种情况的,就属于缓存和数据库的数据不一致问题了。不过,当缓存的读写模式不同时,缓存数据不一致的发生情况不一样,我们的应对方法也会有所不同,所以,我们先按照缓存读写模式,来分别了解下不同模式下的缓存不一致情况。我们可以把缓存分成读写缓存和只读缓存。

    对于读写缓存来说,如果要对数据进行增删改,就需要在缓存中进行,同时还要根据采取的写回策略,决定是否同步写回到数据库中。

    同步直写策略:写缓存时,也同步写数据库,缓存和数据库中的数据一致;

    异步写回策略:写缓存时不同步写数据库,等到数据从缓存中淘汰时,再写回数据库。使用这种策略时,如果数据还没有写回数据库,缓存就发生了故障,那么,此时,数据库就没有最新的数据了。

    所以,对于读写缓存来说,要想保证缓存和数据库中的数据一致,就要采用同步直写策略。不过,需要注意的是,如果采用这种策略,就需要同时更新缓存和数据库。所以,我们要在业务应用中使用事务机制,来保证缓存和数据库的更新具有原子性,也就是说,两者要不一起更新,要不都不更新,返回错误信息,进行重试。否则,我们就无法实现同步直写。

    当然,在有些场景下,我们对数据一致性的要求可能不是那么高,比如说缓存的是电商商品的非关键属性或者短视频的创建或修改时间等,那么,我们可以使用异步写回策略。
    下面我们再来说说只读缓存。对于只读缓存来说,如果有数据新增,会直接写入数据库;而有数据删改时,就需要把只读缓存中的数据标记为无效。这样一来,应用后续再访问这些增删改的数据时,因为缓存中没有相应的数据,就会发生缓存缺失。此时,应用再从数据库中把数据读入缓存,这样后续再访问数据时,就能够直接从缓存中读取了。

    接下来,以 Tomcat 向 MySQL 中写入和删改数据为例,来给你解释一下,数据的增删改操作具体是如何进行的,如下图所示:

    从图中可以看到,Tomcat 上运行的应用,无论是新增(Insert 操作)、修改(Update 操作)、还是删除(Delete 操作)数据 X,都会直接在数据库中增改删。当然,如果应用执行的是修改或删除操作,还会删除缓存的数据 X。

    那么,这个过程中会不会出现数据不一致的情况呢?考虑到新增数据和删改数据的情况不一样,所以我们分开来看。

    1. 新增数据
      如果是新增数据,数据会直接写到数据库中,不用对缓存做任何操作,此时,缓存中本身就没有新增数据,而数据库中是最新值,这种情况符合我们刚刚所说的一致性的第 2 种情况,所以,此时,缓存和数据库的数据是一致的。
    2. 删改数据
      如果发生删改操作,应用既要更新数据库,也要在缓存中删除数据。这两个操作如果无法保证原子性,也就是说,要不都完成,要不都没完成,此时,就会出现数据不一致问题了。这个问题比较复杂,我们来分析一下。

    我们假设应用先删除缓存,再更新数据库,如果缓存删除成功,但是数据库更新失败,那么,应用再访问数据时,缓存中没有数据,就会发生缓存缺失。然后,应用再访问数据库,但是数据库中的值为旧值,应用就访问到旧值了。
    我来举个例子说明一下,可以先看看下面的图片。
    在这里插入图片描述

    应用要把数据 X 的值从 10 更新为 3,先在 Redis 缓存中删除了 X 的缓存值,但是更新数据库却失败了。如果此时有其他并发的请求访问 X,会发现 Redis 中缓存缺失,紧接着,请求就会访问数据库,读到的却是旧值 10。
    你可能会问,如果我们先更新数据库,再删除缓存中的值,是不是就可以解决这个问题呢?我们再来分析下。
    如果应用先完成了数据库的更新,但是,在删除缓存时失败了,那么,数据库中的值是新值,而缓存中的是旧值,这肯定是不一致的。这个时候,如果有其他的并发请求来访问数据,按照正常的缓存访问流程,就会先在缓存中查询,但此时,就会读到旧值了。
    我还是借助一个例子来说明一下。
    在这里插入图片描述

    应用要把数据 X 的值从 10 更新为 3,先成功更新了数据库,然后在 Redis 缓存中删除 X 的缓存,但是这个操作却失败了,这个时候,数据库中 X 的新值为 3,Redis 中的 X 的缓存值为 10,这肯定是不一致的。如果刚好此时有其他客户端也发送请求访问 X,会先在 Redis 中查询,该客户端会发现缓存命中,但是读到的却是旧值 10。
    好了,到这里,我们可以看到,在更新数据库和删除缓存值的过程中,无论这两个操作的执行顺序谁先谁后,只要有一个操作失败了,就会导致客户端读取到旧值。我画了下面这张表,总结了刚刚所说的这两种情况。
    在这里插入图片描述

    问题发生的原因我们知道了,那该怎么解决呢?

    如何解决数据不一致问题?
    首先,我给你介绍一种方法:重试机制。
    具体来说,可以把要删除的缓存值或者是要更新的数据库值暂存到消息队列中(例如使用 Kafka 消息队列)。当应用没有能够成功地删除缓存值或者是更新数据库值时,可以从消息队列中重新读取这些值,然后再次进行删除或更新。
    如果能够成功地删除或更新,我们就要把这些值从消息队列中去除,以免重复操作,此时,我们也可以保证数据库和缓存的数据一致了。否则的话,我们还需要再次进行重试。如果重试超过的一定次数,还是没有成功,我们就需要向业务层发送报错信息了。
    下图显示了先更新数据库,再删除缓存值时,如果缓存删除失败,再次重试后删除成功的情况,你可以看下。
    在这里插入图片描述

    刚刚说的是在更新数据库和删除缓存值的过程中,其中一个操作失败的情况,实际上,即使这两个操作第一次执行时都没有失败,当有大量并发请求时,应用还是有可能读到不一致的数据。
    同样,我们按照不同的删除和更新顺序,分成两种情况来看。在这两种情况下,我们的解决方法也有所不同。

    情况一:先删除缓存,再更新数据库。
    假设线程 A 删除缓存值后,还没有来得及更新数据库(比如说有网络延迟),线程 B 就开始读取数据了,那么这个时候,线程 B 会发现缓存缺失,就只能去数据库读取。这会带来两个问题:

    1. 线程 B 读取到了旧值;
    2. 线程 B 是在缓存缺失的情况下读取的数据库,所以,它还会把旧值写入缓存,这可能会导致其他线程从缓存中读到旧值。

    等到线程 B 从数据库读取完数据、更新了缓存后,线程 A 才开始更新数据库,此时,缓存中的数据是旧值,而数据库中的是最新值,两者就不一致了。
    我用一张表来汇总下这种情况。
    在这里插入图片描述

    这该怎么办呢?我来给你提供一种解决方案。

    在线程 A 更新完数据库值以后,我们可以让它先 sleep 一小段时间,再进行一次缓存删除操作。
    之所以要加上 sleep 的这段时间,就是为了让线程 B 能够先从数据库读取数据,再把缺失的数据写入缓存,然后,线程 A 再进行删除。所以,线程 A sleep 的时间,就需要大于线程 B 读取数据再写入缓存的时间。这个时间怎么确定呢?建议你在业务程序运行的时候,统计下线程读数据和写缓存的操作时间,以此为基础来进行估算。
    这样一来,其它线程读取数据时,会发现缓存缺失,所以会从数据库中读取最新值。因为这个方案会在第一次删除缓存值后,延迟一段时间再次进行删除,所以我们也把它叫做“延迟双删”。
    下面的这段伪代码就是“延迟双删”方案的示例,你可以看下。

    redis.delKey(X)
    db.update(X)
    Thread.sleep(N)
    redis.delKey(X)
    

    情况二:先更新数据库值,再删除缓存值。
    如果线程 A 删除了数据库中的值,但还没来得及删除缓存值,线程 B 就开始读取数据了,那么此时,线程 B 查询缓存时,发现缓存命中,就会直接从缓存中读取旧值。不过,在这种情况下,如果其他线程并发读缓存的请求不多,那么,就不会有很多请求读取到旧值。而且,线程 A 一般也会很快删除缓存值,这样一来,其他线程再次读取时,就会发生缓存缺失,进而从数据库中读取最新值。所以,这种情况对业务的影响较小。
    我再画一张表,带你总结下先更新数据库、再删除缓存值的情况。
    在这里插入图片描述

    好了,到这里,我们了解到了,缓存和数据库的数据不一致一般是由两个原因导致的,我给你提供了相应的解决方案。

    • 删除缓存值或更新数据库失败而导致数据不一致,你可以使用重试机制确保删除或更新操作成功。
    • 在删除缓存值、更新数据库的这两步操作中,有其他线程的并发读操作,导致其他线程读取到旧值,应对方案是延迟双删。
    展开全文
  • 缓存击穿 原因:热点数据缓存失效 解决:热点访问数据查询时加锁、并放入缓存缓存穿透 原因:访问存在数据 解决:布隆过滤器 缓存雪崩 缓存不一致 ...

    缓存击穿

    原因:热点数据缓存失效

    解决:热点访问数据查询时加锁、并放入缓存中

     

     

    缓存穿透

    原因:访问不存在数据

    解决:布隆过滤器

     

     

    缓存雪崩

    原因:导致数据库宕机

    解决:

    1. 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
    2. 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
    3. 设置热点数据永远不过期。

     

     

    缓存不一致

     

    展开全文
  • 缓存不一致问题分析

    2020-04-01 21:15:27
    缓存不一致问题 缓存更新的4种策略 1.给缓存设置过期时间。这样缓存和数据库能保持最终一致性。 2.先更新数据库,再更新缓存。问题:1.线程A更新了数据库,线程B再更新了数据库。线程B更新了缓存,线程A更新了...

    缓存不一致问题

    缓存更新的4种策略

    • 1.给缓存设置过期时间。这样缓存和数据库能保持最终一致性。

    • 2.先更新数据库,再更新缓存。问题:1.线程A更新了数据库,线程B再更新了数据库。线程B更新了缓存,线程A更新了缓存。最终结果是缓存以线程A更新的为准,数据库以线程B更新的为准,出现了不一致。2.更新了数据库就去更新缓存,实际上可能这些缓存不是热点数据,且这些缓存需要复杂的计算生成,浪费了性能。3.更新了数据库,然后更新缓存的时候程序出问题了中止了,那么也会造成数据不一致问题。

    • 3.先删除缓存,再更新数据库。问题:同时有一个请求A进行更新操作,另一个请求B进行查询操作。那么会出现如下情形:(1)A进行写操作,删除缓存(2)B查询发现缓存不存在(3)B去数据库查询得到旧值(4)B将旧值写入缓存(5)A将新值写入数据库。

    解决方案:延时双删除,伪代码如下

    public void write(String key,Object data){
    
      redis.delKey(key);
    
      db.updateData(data);
        // 延时一秒的目的是,让读线程B更新完缓存,线程A再去删除。
    
      Thread.sleep(1000);
    
      redis.delKey(key);
    
    }
    
    • 4.先更新数据库,再删除缓存。

    假设这会有两个请求,一个请求A做查询操作,一个请求B做更新操作,那么会有如下情形产生

    (1)缓存刚好失效

    (2)请求A查询数据库,得一个旧值

    (3)请求B将新值写入数据库

    (4)请求B删除缓存

    (5)请求A将查到的旧值写入缓存

    延时删除可以解决这个问题,即让读线程把业务走完再去删除。

    删除也可能有失败的情况,需要启用重试机制。

    删除失败后,丢入消息队列,由另外的线程从队列中获取去删除。

    展开全文
  • 因为程序执行都在cpu中,但是如果没有高速缓存,cpu大部分的时间都用来了读取内存的数据。 从而Cpu有 高速缓存,在运行指令前,会把相关需要的数据提前拷贝到cpu,运算完成后在刷回内存里。   高速缓存主要提前...
  • 还是先删除缓存,再写库,都有可能出现数据不一致的情况: 1.如果删除了缓存,还没有来得及写数据库,另一个线程就来读取,发现缓存为空,则去数据库中读取数据写入缓存,此时缓存中为脏数据。 2.如果先写了库,在...
  • 缓存数据一致解决方案之双写模式(写数据库的同时把缓存中的数据也改了) 解决脏读方法: 加读写锁 缓存数据一致解决方案之失效模式(在修改数据库的同时删除缓存数据,下次请求时从数据库获取) 可见,最后读缓存...
  • 保证最终一致性的解决方案是缓存设置过期时间。一下方案讨论的是依赖于给缓存设置过期时间的情况。 方案一:先更新缓存,再更新数据库 推荐。 先更新缓存若更新数据库失败,还需再更新缓存。 方案二:先更新...
  • 缓存与数据库不一致主要有以下几点:一、数据库主从不一致二、缓存与数据库不一致三、问题分析四、不一致优化 一、数据库主从不一致 如下图所演示,无缓存时,数据库主从不一致 该图发生的场景是,写后立刻读 主库...
  • 缓存一致解决方法

    2021-10-08 18:51:05
    对于缓存 + 数据库读写,有个经典的Cache ...一致性问题 假设更新数据库成功,接下来还没来得及删除缓存,或者删除缓存失败,此时其他线程进来读的就是脏数据。既然删除缓存失败会导致脏数据,那么就得想办法能让它
  • 由于CPU的读写速度远远大于内存的读写速度,所以为了解决...这就是CPU缓存不一致导致的问题。 解决方法: 总线加锁,由于CPU和其他组件都是通过总线(数据总线,控制总线,地址总线)来通信,不过如果采用总线加锁的.
  • 缓存一致性需要保证的是,当缓存中有值的时候,数据库的值必须与缓存一致。 根据是否接收写请求,可以将缓存分为读写缓存和只读缓存。两种发生缓存一致的情况不同,需要分开来应对。 在只读缓存中,新增数据会直接...
  • 缓存一致性的产生原因是为了提高系统的吞吐量,通常会把一些即时性、数据强一致性要求高的,或者访问量大且更新频率高(读多写少) 的数据从数据库中读取并放到缓存中,提高系统的IO速度,从而提高吞吐量。...
  • 缓存一致解决方案

    2020-12-04 17:01:44
    缓存一致解决方案
  • 首先我们通过明确其中的概念,再来分析解决方案,这里的缓存我们已redis为例,当然其他的缓存技术,出现的问题以及解决思路大致是一样缓存穿透:是指查询一个一定存在的数据,通常我们都会先查缓存,缓存中没有查...
  • 缓存与数据库不一致

    2018-07-14 10:15:41
    》中的方案,都会遇到缓存与数据库不一致的问题。今天聊聊这个问题。 一、数据库主从不一致先回顾下,无缓存时,数据库主从不一致问题。如上图,发生的场景是,写后立刻读:(1)主库一个写请求(主从没同步完成)...
  • 行业分类-设备装置-为缓存一致处理缓存回写和缓存淘汰
  • WEB缓存_如何解决缓存一致

    千次阅读 2017-12-09 15:44:56
    一、如何解决一致性问题 一个总的原则 一旦数据库更新了,就把原来的缓存失效掉 有时候要做到这一点是很困难的,似乎听起来很可笑,但是当系统规模达到一定程度的时候,这个问题就会凸显。在一个大的团队中,每个...
  • db和缓存的先后问题在日常代码中是很常见的,现在来分析一下每种情景的细节 首先排除定时删除缓存的这种操作,因为这样不会有双写问题。 因为不管有多少线程, (1)缓存没失效的时候都是走的缓存; (2)然后...
  • 预防变更数据库导致缓存不一致问题 坑重现:在做一个数据库结构变更时,表tableA增加了一个字段columnA,原先的缓存与当前缓存实体的结构不一致 事件还原:优惠券模板表spec实体类specDO有属性值fileldA,fileldB等...
  • 这个业务场景,主要是解决读数据从Redis缓存,一般都是按照下图的流程来进行业务操作。 读取缓存步骤一般没有什么问题,但是一旦涉及到数据更新:数据库和缓存更新,就容易出现缓存(Redis)和数据库(MyS...
  • 在大并发的情况下,同时操作数据库和缓存,有可能出现数据不一致的情况 , 那碰到这种情况我们该怎么处理呢? 下面模拟几个场景及对应的解决方案,供大家参考~ 双写不一致 读写并发不一致 ...
  • 如果缓存有多个副本,多个缓存副本里的数据也有可能会发生不一致现象。 原因分析 不一致的问题大多跟缓存更新异常有关。比如更新 DB 后,写缓存失败,从而导致缓存中存的是老数据。另外,如果系统采用一致性 Hash ...
  • 缓存异常——如何解决缓存和数据库的数据不一致问题? 缓存和数据库的一致性问题是需要得到保证的,应用从缓存中读取的必须是最新数据,不然就会产生严重的错误,那么我们先看看缓存与数据库之间的不一致是如何产生...
  • 由于卡顿等原因,导致了缓存2走到了缓存1的前面,这样数据库的数据得到的和缓存中查询的不一致 脏数据的问题,在数据稳定之后,缓存过期后,重新查询数据库便可以达到正常的数据,达到数据的一致性。 失效模式:...
  • 这次就来介绍一下Redis的缓存一致性的问题。 对于缓存和数据库的更新操作,主要分为以下两种 先删除缓存,再更新数据库 先更新数据库,再删除缓存 首先可能会带来疑惑的点是,为什么这里是删除缓存而不是更新缓存...
  • 一般常用的缓存方案有两种: 第一种 读的时候,先读缓存缓存没有的话,读数据库,取出数据后放入缓存,同时返回响应。 更新的时候,先删除缓存,在更新数据库。 第二种 读的时候,先读缓存缓存没有的话,读...
  • 缓存穿透,缓存击穿,缓存雪崩解决方案分析

    万次阅读 多人点赞 2017-01-06 11:12:50
    缓存穿透是指查询一个一定存在的数据,由于缓存命中时被动写的,并且出于容错考虑,如果从存储层查到数据则写入缓存,这将导致这个存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,...
  • 缓存与数据库不一致解决方案

    万次阅读 2016-08-22 16:38:00
    (1)啥时候数据库和缓存中的数据会不一致 (2)不一致优化思路 (3)如何保证数据库与缓存一致性   一、需求缘起 上一篇《缓存架构设计细节二三事》(点击查看)引起了广泛的讨论,其中有一个...
  • 1、最初级的缓存不一致问题以及解决方案问题:先修改数据库,再删除缓存,如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据出现不一致解决思路:  先删除缓存,再修改数据库,如果删除缓存...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 792,891
精华内容 317,156
关键字:

缓存不一致怎么解决的