缓存与数据库结合使用的痛点
为什么缓存与数据库要结合使用,使用缓存的目的是为了减少用户请求穿透到服务端和数据库,尽量将资源放置在离用户更近的地方,让用户能够更快得到应答。在一些活动大促、秒杀场景中,经常会面临突然的流量爆发,造成系统的高并发访问。例如秒杀商品的详情页内容提前预热到缓存中,能缓解数据库读取压力。但在高并发下,缓存与数据库的使用存在两个痛点
痛点一:在高并发访问下,缓存的使用会引发穿透、雪崩、击穿等问题,导致流量穿透缓存层直接压到数据库,如果超出了数据库系统承载能力,会造成宕机。
痛点二:缓存与数据库如何更新同步,才能保持两边数据是一致的。是先更新数据库后更新缓存,还是先更新缓存再更新数据库。更新完缓存后,更新数据库失败,怎么办?又或者更新数据库后,在更新缓存时,有另一个业务线程去更新数据库,此时更新缓存里的数据是脏数据,和数据库不一致。
缓存穿透是用户大量访问一个不存在的key建引起,请求穿透到数据库。
缓存击穿是一个热点key突然缓存时间到期失效,大量请求落到数据库。
缓存雪崩是指大范围key在同一时间失效,请求同一时间落到数据库。
以上缓存问题,假设缓存不设置过期失效时间,请求不会回溯到数据库,也就不会发生击穿、雪崩等问题。缓存穿透如果访问不存在key,直接返回空值,请求也不会回溯到数据库。但如果不回溯到数据库,缓存如何更新,例如在更新数据库时,主动更新缓存。这个方案看似合理,但在更新缓存时,存在并发更新的问题,后一个更新缓存的值可能被前一个值覆盖,这是因为缓存更新没类似数据库的事务隔离机制,更新操作是一个非幂等操作。
通过消息队列将更新缓存操作串行处理,可以解决并发更新的问题,比如线程A、线程B在并发更新数据库时,利用数据库事务隔离机制避免脏读、不可重复读。然后把数据标识写入消息队列,接下来消费消息队列,再通过数据标识去读取数据库相应数据并刷新缓存。虽然异步操作无法保证顺序,但最终是通过数据标识获取新的数据刷新缓存,所以不会出现前后步骤值被相互覆盖的问题。
![](https://img2022.cnblogs.com/blog/2978104/202209/2978104-20220911231809504-2091596930.png)