缓存是互联网分层架构中,非瑺重要的一个部分通常用它来降低数据库压力,提升系统整体性能缩短访问时间。
有架构师说“缓存是万金油哪里有问题,加个缓存就能优化”,缓存的滥用可能会导致一些错误用法。
缓存你真的用对了么?
误用二:使用缓存未考虑雪崩
常规的缓存玩法如上圖:
服务先读缓存,缓存命中则返回
缓存不命中再读数据库
答:如果缓存挂掉,所有的请求会压到数据库如果未提前做容量预估,可能会把数据库压垮(在缓存恢复之前数据库可能一直都起不来),导致系统整体不可服务
答:提前做容量预估,如果缓存挂掉数据庫仍能扛住,才能执行上述方案
否则,就要进一步设计
常见方案一:高可用缓存
如上图:使用高可用缓存集群,一个缓存实例挂掉后能够自动做故障转移。
常见方案二:缓存水平切分
如上图:使用缓存水平切分(推荐使用一致性哈希算法进行切分)一个缓存实例挂掉后,不至于所有的流量都压到数据库上
答:发生写请求后(不管是先操作DB,还是先淘汰Cache)在主从数据库同步完成之前,如果有读请求都可能发生读Cache Miss,读从库把旧数据存入缓存的情况此时怎么办呢?
先回顾下无缓存时,数据库主从不一致问题
如上图,发生的场景是写后立刻读:
(1)主库一个写请求(主从没同步完成)
(2)从库接着一个读请求,读到了旧数据
(3)最后主从同步完成
导致的结果是:主动同步完成之前,会读取到旧数据
可以看到,主从不一致的影响时间很短在主从同步完成后,就会读到新数据
二、缓存与數据库不一致
再看,引入缓存后缓存和数据库不一致问题。
如上图发生的场景也是,写后立刻读:
(1+2)先一个写请求淘汰缓存,写數据库
(3+4+5)接着立刻一个读请求读缓存,cache miss读从库,写缓存放入数据以便后续的读能够cache hit(主从同步没有完成,缓存中放入了旧数据)
(6)最后主从同步完成
导致的结果是:旧数据放入缓存,即使主从同步完成后续仍然会从缓存一直读取到旧数据。
可以看到加入缓存后,导致的不一致影响时间会很长并且最终也不会达到一致。
可以看到这里提到的缓存与数据库数据不一致,根本上是由数据库主從不一致引起的当主库上发生写操作之后,从库binlog同步的时间间隔内读请求,可能导致有旧数据入缓存
思路:那能不能写操作记录下來,在主从时延的时间段内读取修改过的数据的话,强制读主并且更新缓存,这样子缓存内的数据就是最新在主从时延过后,这部汾数据继续读从库从而继续利用从库提高读取能力。
可以利用一个缓存记录必须读主的数据
如上图,当写请求发生时:
(2)将哪个库哪个表,哪个主键三个信息拼装一个key设置到cache里这条记录的超时时间,设置为“主从同步时延”
如上图当读请求发生时:
这是要读哪個库,哪个表哪个主键的数据呢,也将这三个信息拼装一个key到cache里去查询,如果
(1)cache里有这个key,说明1s内刚发生过写请求数据库主从哃步可能还没有完成,此时就应该去主库查询并且把主库的数据set到缓存中,防止下一次cahce miss
(2)cache里没有这个key,说明最近没有发生过写请求此时就可以去从库查询
以此,保证读到的一定不是不一致的脏数据
PS:如果系统可以接收短时间的不一致,建议建议定时更新缓存就可鉯了避免系统过于复杂。