缓存的使用模式
最近在看公司的库存编年史,看到公司在库存上的架构演变,看到关于缓存的地方,发现对于缓存的设计有点忘了,整理总结一下。
cache aside 最常见
就是最常见的有缓存读缓存,没缓存读db,回写缓存,更新db需要失效缓存
问题
这里需要注意的是并发情况下的顺序问题
- 先更新db,后更新缓存:后更新,而不是失效缓存的问题是 如果有并发的写的话,因为回写缓存的网络延迟不好控制,会存在先更新的数据后写入缓存带来的脏数据问题
- 先删除缓存,后更新db:还是并发问题,这回担心的是并发有读取的话,并发的读会把脏数据回写缓存中,在删除缓存到更新db这段时间的所有读都会读db,只要读db 先于写db完成,回写的就是脏数据。
- 先更新db,后失效缓存:这种方式是cache aside的推荐方式,但是实际上这种也是有并发风险。不过这种方式的风险发生条件比较苛刻:缓存已经失效->读首先发生->发生读db->此时更新db->db更新成功并执行失效缓存,此时读db请求因为网络时延才返回->读请求回写缓存->脏数据。
- 这种说条件苛刻,是因为需要 缓存失效、读db先于写db完成,读请求写缓存后于写请求失效缓存。但其实,发生是可能的
变形使用
这边在公司听闻到两个使用cache aside,但是其中加了改版的设计,只做记录
- 在读不到数据的时候,还会写缓存,会写一个特定值进去,是为了避免外部有人恶意用不存在的数据刷接口造成缓存命中率大幅下跌和db的查询。这种可以缓存写特定值的自然不是所有场景通用,是特殊情况。
- 还有一种,使用了更新db,后更新缓存的方式。为了避免上面提到的第二个问题,这里更新缓存不是直接拿入参更新db的值来更新的缓存,而是主动查询最新记录,把最新记录更新到缓存中。相当于主动模拟了一把更新db,缓存失效后的读请求。但是上面第三个问题也说了,即便如此,也可能因为位置的网络时延等原因,导致并发写,并发更新缓存,更新的是脏数据。面对这种问题,该解决方式是,临界值的时候,再次更新缓存,其他时候,容忍这种脏数据。
read/write through 需要cache提供支持
这种模式和aside的区别就是,所有对db的操作都不是有应用直接操作的,应用只负责和缓存交互,缓存负责把没命中的数据查出来,把更新的数据写入db。所有这些过程都是同步完成的,所以称之为through,“透过缓存”。
这种模式是需要缓存提供了方式可以和db交互。缓存层不单单是存储,还要有上层的管控系统。
write behind 有丢数据风险
相比于 write through,write behind强调的是写的异步性,也就是应用更新到缓存的数据,缓存不会同步更新到db,而是在不知道什么时候,异步批量的更新到db去。
这种模式的好处是性能好,对db更友好,坏处也很明显,缓存突然挂了,数据没持久化就gg了。而且这种模式也需要缓存在存储之外有管控系统,能够识别脏数据,定时回刷db。