案例1,缓存和DB的数据不同步(不一致)
后台系统CRM更新产品数据到DB,产品系统收到异步消息通知后,更新最新数据到缓存。这是一个最常见的缓存应用场景,我相信很多团队都是这样用的。在这个Case里容易出现的问题在于,如果产品系统收到消息后服务挂掉了,缓存没有正常更新,就出现缓存与DB的数据不同步,前端系统一直不能读到最新数据,导致业务异常。
解决方案:
1. mq消费端创建个本地消息表,对于消费失败的消息进行重试
2. 系统要有缓存更新的报警机制,方便更新失败或者重试超时后,可以人工介入进行补偿。
案例2, 同时被两次请求update时,db数据被错误覆盖
CRM系统更新了某个用户的Profile, 保存更新数据库后,通过MQ通知用户系统更新缓存,由于是异步更新延迟,在缓存更新前,用户系统收到前端的请求,读取了当前缓存里的用户数据,做了修改,并更新到DB中。出现的结果就是数据库里的CRM的更新被错误覆盖。
解决方案:缓存里的数据有一个标志位可以作为更新数据库数据的依据(Update_time or Version), 如果缓存里数据时间与数据库时间不能匹配,意味着另外一个服务更新了该数据,那么就先从DB里读取最新数据版本,然后在新版本上提交数据。
案例3, 并发查询缓存中同一数据,如果缓存没命中,导致DB瞬时被打爆
做促销活动的时候,存在大量用户的并发访问某一个特定商品,该商品数据缓存失效,或者做了数据更改,但是对应缓存还没有更新,那么所有这些访问将同时直接被作用到DB上。
解决方案:做一个计数器或者锁,如果发现某个KEY缓存没有命中,那么在计数器+1, 然后访问数据库,拿到结果更新缓存,清理掉计数器中的key。 在这个过程中,如果有第二个线程或者更多的线程需要访问这个KEY时,发现计数器的值>1 或者被加锁, 那么wait, 直到计数器清理掉。
案例4, 缓存没有设置默认值,被攻击,缓存一直保持在被“穿透”状态
这个情况,和案例3比较类似,都是缓存无法命中,但不一样的地方在于,数据的KEY值是无法控制的,所以没法简单的用计数器和锁来处理, 比方,被人为攻击,制造的大量的无效userID访问。
解决方案:所有没有在缓存的KEY,全部分配一个默认VALUE “UNKOWN-KEY” ,具体是什么情况下,将默认值分配给没有命中的KEY, 这个可以根据自己的业务系统来定,比方说,可以根据特定的IP段,或者没有命中的总次数等,然后我们就可以 决定是否继续访问DB还是直接返回默认值给前端,拒绝本次数据访问。这种做法的核心在于,每次数据访问,都会有缓存结果返回,根据系统的情况来决定是否要进一步访问DB。
总结,今天列举的这几个案例,归纳起来,可以总结为以下几点:
1. 保证缓存同步
2. 减少缓存并发
3. 杜绝缓存穿透
缓存与背后的DB是相互依存的关系,缓存系统的设计原则,就是将访问的异常处理或者压力尽可能的前置处理掉,将DB还原成它最初本来的存储功能。
来源:oschina
链接:https://my.oschina.net/garlicts/blog/3211306