缓存同步、如何保证缓存一致性、缓存误用(^_^)

缓存误用

缓存,是互联网分层架构中,非常重要的一个部分,通常用它来降低数据库压力,提升系统整体性能,缩短访问时间。

有架构师说“缓存是万金油,哪里有问题,加个缓存,就能优化”,缓存的滥用,可能会导致一些错误用法。

缓存,你真的用对了么?

误用二:使用缓存未考虑雪崩

常规的缓存玩法,如上图:


服务先读缓存,缓存命中则返回
缓存不命中,再读数据库

什么时候会产生雪崩?
答:如果缓存挂掉,所有的请求会压到数据库,如果未提前做容量预估,可能会把数据库压垮(在缓存恢复之前,数据库可能一直都起不来),导致系统整体不可服务。

如何应对潜在的雪崩?
答:提前做容量预估,如果缓存挂掉,数据库仍能扛住,才能执行上述方案。

否则,就要进一步设计。

常见方案一:高可用缓存


 

如上图:使用高可用缓存集群,一个缓存实例挂掉后,能够自动做故障转移。

 

常见方案二:缓存水平切分


缓存同步、如何保证缓存一致性、缓存误用(^_^)_第1张图片

 

如上图:使用缓存水平切分(推荐使用一致性哈希算法进行切分),一个缓存实例挂掉后,不至于所有的流量都压到数据库上。

数据不一致解决场景及解决方案

答:发生写请求后(不管是先操作DB,还是先淘汰Cache),在主从数据库同步完成之前,如果有读请求,都可能发生读Cache Miss,读从库把旧数据存入缓存的情况。此时怎么办呢?

一、数据库主从不一致


先回顾下,无缓存时,数据库主从不一致问题。


缓存同步、如何保证缓存一致性、缓存误用(^_^)_第2张图片

 

如上图,发生的场景是,写后立刻读:


(1)主库一个写请求(主从没同步完成)
(2)从库接着一个读请求,读到了旧数据
(3)最后,主从同步完成
 

导致的结果是:主动同步完成之前,会读取到旧数据。

 

可以看到,主从不一致的影响时间很短,在主从同步完成后,就会读到新数据。

 

二、缓存与数据库不一致


再看,引入缓存后,缓存和数据库不一致问题。


缓存同步、如何保证缓存一致性、缓存误用(^_^)_第3张图片

 

如上图,发生的场景也是,写后立刻读:


(1+2)先一个写请求,淘汰缓存,写数据库

 

(3+4+5)接着立刻一个读请求,读缓存,cache miss,读从库,写缓存放入数据,以便后续的读能够cache hit(主从同步没有完成,缓存中放入了旧数据)

 

(6)最后,主从同步完成

 

导致的结果是:旧数据放入缓存,即使主从同步完成,后续仍然会从缓存一直读取到旧数据。

 

可以看到,加入缓存后,导致的不一致影响时间会很长,并且最终也不会达到一致。

 

三、问题分析


可以看到,这里提到的缓存与数据库数据不一致,根本上是由数据库主从不一致引起的。当主库上发生写操作之后,从库binlog同步的时间间隔内,读请求,可能导致有旧数据入缓存。

 

思路:那能不能写操作记录下来,在主从时延的时间段内,读取修改过的数据的话,强制读主,并且更新缓存,这样子缓存内的数据就是最新。在主从时延过后,这部分数据继续读从库,从而继续利用从库提高读取能力。

四、不一致解决方案


选择性读主


可以利用一个缓存记录必须读主的数据。


缓存同步、如何保证缓存一致性、缓存误用(^_^)_第4张图片

 

如上图,当写请求发生时:


(1)写主库
(2)将哪个库,哪个表,哪个主键三个信息拼装一个key设置到cache里,这条记录的超时时间,设置为“主从同步时延”
 

PS:key的格式为“db:table:PK”,假设主从延时为1s,这个key的cache超时时间也为1s。

 

缓存同步、如何保证缓存一致性、缓存误用(^_^)_第5张图片

 

如上图,当读请求发生时:


这是要读哪个库,哪个表,哪个主键的数据呢,也将这三个信息拼装一个key,到cache里去查询,如果,


(1)cache里有这个key,说明1s内刚发生过写请求,数据库主从同步可能还没有完成,此时就应该去主库查询。并且把主库的数据set到缓存中,防止下一次cahce miss。
(2)cache里没有这个key,说明最近没有发生过写请求,此时就可以去从库查询

以此,保证读到的一定不是不一致的脏数据。

 

PS:如果系统可以接收短时间的不一致,建议建议定时更新缓存就可以了。避免系统过于复杂。

你可能感兴趣的:(java)