pandening
12/25/2018 - 7:22 AM

cache , local, remote;

cache , local, remote;

缓存client套路

1、缓存不能读到脏数据 2、缓存可能读到过期数据,是否可以容忍一段时间的不一致性,但是可以保证最终一致性(只是达到最终一致性的时间长短不一样)

client缓存套路及存在的问题:

  • 先更新数据库,再更新缓存

存在的问题:可能读到脏数据 问题分析:有两个线程A和B进行更新操作

(1)、线程A更新了DB,key=1 value=2 (2)、线程B更新了DB,key=1 value=3 (3)、线程B更新了cache,key=1 value=3 (4)、线程A更新了cache,key=1 value=2

这样就导致了DB和缓存数据不一致的问题,因为DB里面key=1 value=3,而缓存里面是key=1 value=2

解决办法:时间戳 + 版本号(ABA问题)

  • 先删除cache,再更新db

存在的问题:脏数据 问题分析:线程A执行更新操作,线程B执行查询操作 (1)、线程A进行写操作,key=1 value=2,先删除了缓存key=1 value=1 (2)、线程B查询缓存发现缓存不存在,然后去db查询得到key=1 value=1,线程B将key=1 value=1写到缓存 (3)、线程A此时将key=1 value=2写到db

这样就造成了db和缓存存储的不一致,db里面key=1 value=2,cache里面则是key=1 value=1 解决办法: (1)、先删除cache,再更新db,再回写cache(和第一种方案一样的问题) (2)、先删除cache,再更新db,再删除cache(双删,第二次可以异步执行)

  • 先更新db,再删除cache

问题:脏数据(小概率) 问题分析:一个线程A做查询,一个线程B做更新 (1)、缓存刚好失效 (2)、线程A查询db,得到key=1 value=1 (3)、线程B将新值key=1 value=2写入db (4)、线程B更新完db之后删除了cacahekey=1 value=1 (5)、线程A将查到的key=1 value=1写入缓存

这个时候,db和缓存存储的就是不一致的,db是key=1 value=2,cache是key=1 value=1

  • 异步更新中间件

比如databus监听db binlog,然后更新缓存。

缓存穿透

查询肯定不存在的数据,缓存不命中,走到db,db挂

方案: (1)、BloomFilter,布隆过滤器,将所有可能存在的key映射到一个足够大的bitmap中去,不存在的key会被bitmap拦截到;但是不存在的key也可能被映射到这块bitmap中去,但是很少;该方法非常高效。 (2)、缓存空结果 + 过期时间:将不存在的结果也缓存起来,设置一个可以容忍的过期时间;实现起来非常简单。

缓存雪崩

缓存的多个key设置了相同的过期时间,某一时刻多个缓存key同时失效,请求全部落到db,db奔溃。

方案: (1)、加锁查db;一个请求查询key,如果miss,则先lock该key,其他请求在该key上等待,直到被唤醒,然后再check一下key是否存在(double check);这种策略下就是说只可能有一个线程会去db加载数据缓存起来。 (2)、错开过期时间,这是最好实现的方式,别让大多数缓存一起失效 (3)、预先加载,当发现缓存快到期的时候异步加载

热点击穿

某些时间点,某些Key会被高并发地访问,形成热点Key;热点Key恰好在这时过期了,大量并发打到DB;或是超高并发单Key(一般是hash到单台机上),打满单机的网卡/cpu。

(1)、预判:在value内部设置一个过期时间t1,t1比实际缓存失效时间t2要小,当发现t1已过期,那么就通知重新加载缓存,这样就可以无缝切换新旧值了。 (2)、预热:将热数据提前加载到缓存系统,所有请求直接请求缓存,后台异步线程更新缓存

总结:都是为了让请求直接请求到缓存,而没有到db,异步线程来做db->cache的工作