一致性问题多发生在数据更新阶段,目前有2种: **(1) 先删除缓存,再更新数据库** **(2) 先更新数据库,再删除缓存** # 一、为什么不能更新缓存而要删除 因为并发情况下的执行顺序与预期不一致,会出现旧值覆盖新值的情况,如图所示 ![[Snipaste_2023-03-01_17-52-19.png]] 线程1 先发起的更新请求,过了一段时间线程2也发起更新,但是由于线程2比线程1先执行完,当线程1执行完成后会覆盖线程2更新的缓存值,就导致了库与缓存数据不一致问题。因此并发环境下不要更新缓存。 # 二、为什么不能先删除缓存,再更新数据库 ![[Snipaste_2023-03-01_17-52-19 1.png]] 如图所示,读请求如果没有命中缓存,会先查库再更新缓存。假如先删除缓存,再更新库,如下图所示: ![[Snipaste_2023-03-01_17-52-19 2.png]] 当写、读两个线程同时执行,由于读先执行完,读线程将缓存的val更新为a1,但是库中实际值是a2,此时出现不一致情况。 # 三、Cache-Aside Pattern Cache-Aside Pattern,即旁路缓存模式,它的提出是为了尽可能地解决缓存与数据库的数据不一致问题。 通俗的说就是先更新库,再删除缓存,理想情况下会出现极短的不一致问题。 ![[Snipaste_2023-03-01_17-52-19 3.png]] 如图所示,线程2 的查询在线程1之前完成,将缓存val设为a1,随后线程1删除缓存,当下一次查询时缓存没有命中,就会将缓存val设为a2,实现了一致性。 ## 3.1 不一致场景 ![[Snipaste_2023-03-01_17-52-19 4.png]] 当读线程比写线程执行慢的时候又会产生不一致情况,直到缓存过期或下一次更新。因此引入了延迟双删策略。只需要保证,删除动作在写动作之后完成就能保证一致性。 ## 3.2 延迟双删 ![[Snipaste_2023-03-01_17-52-19 6.png]] 引入了第二次删除,将要删除的Key交给定时任务或MQ,只要线程2的写缓存操作在设定的延时范围内执行完成,就能保证一致性。延迟时间需要更新业务来设置。 ## 3.3 改进延迟双删 假如删除缓存的动作失败了,需要加入重试机制。