MENU

关于缓存更新的总结

October 21, 2020 • Read: 350 • Java

对于读多写少的应用场景,我们经常使用缓存来进行优化。

当数据发生变化的时候:

(1)是更新缓存中的数据,还是淘汰缓存中的数据呢?

(2)是先操纵数据库中的数据再操纵缓存中的数据,还是先操纵缓存中的数据再操纵数据库中的数据呢?

更新缓存 VS 淘汰缓存

什么是更新缓存:数据不但写入数据库,还会写入缓存

什么是淘汰缓存:数据只会写入数据库,不会写入缓存,只会把数据淘汰掉

更新缓存的优点:缓存不会增加一次miss,命中率高

淘汰缓存的优点:直接删除,简单快捷

如何选择首先取决于更新缓存的复杂度

如果要更新的value是通过很复杂的数据计算得出来的,相对来说淘汰缓存简单,带来的副作用是高并发情况下会产生缓存穿透

先操作数据库 vs 先操作缓存

首先我们先了解一下更新缓存的的Design Pattern

Cache Aside Pattern

比较常用,细节如下:

  • 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
  • 命中:应用程序从cache中取数据,取到后返回。
  • 更新:先把数据存到数据库中,成功后,再让缓存失效。

Cache-Aside-Design-Pattern-Flow-Diagram-e1470471723210

Updating-Data-using-the-Cache-Aside-Pattern-Flow-Diagram-1-e1470471761402

Cache Aside也会有并发问题,比如,一个是读操作,但是没有命中缓存,然后就到数据库中取数据,此时来了一个写操作,写完数据库后,让缓存失效,然后,之前的那个读操作再把老的数据放进去,所以,会造成脏数据。实际出现的概率很低,发生条件是读缓存失效,并且并发这一个写操作,实际数据库的写操作会比读操作慢的多,就是说读操作要先进入数据库而且要比写操作的的时间长,这样才会出现读出来的脏数据更新到缓存。

Read/Write Through Pattern

  • Read Through

Read Through 查询操作中更新缓存,也就是说,当缓存失效的时候(过期或LRU换出),Cache Aside是由调用方负责把数据加载入缓存,而Read Through则用缓存服务自己来加载,从而对应用方是透明的。

  • Write Through

Write Through 和 Read Through相仿,在更新数据时发生。当有数据更新的时候,如果没有命中缓存,直接更新数据库,然后返回。如果命中了缓存,则更新缓存,然后再由Cache自己更新数据库(这是一个同步操作)

460px-Write-through_with_no-write-allocation.svg_

Write Behind Caching Pattern

更新数据的时候,只更新缓存,不更新数据库,而我们的缓存会异步地批量更新数据库。这个设计的好处就是让数据的I/O操作飞快无比(因为直接操作内存嘛 ),因为异步,Write Behind还可以合并对同一个数据的多次操作,所以性能的提高是相当可观的。

数据不是强一致性的,而且可能会丢失,因为还没持久化到硬盘上(这里我们不单单讨论redis做为缓存)。当然redis也有持久化策略可以保证数据最少丢失甚至是不丢之,强一致性和高性能,高可用和高性性是有冲突的。

另外,Write Behind实现逻辑比较复杂,因为他需要标记有哪数据是被更新了的,需要刷到持久层上。这个就有点类似mysql的buffpool中的脏页刷盘。

Write-back_with_write-allocation