刷帖子,看到有佬吐槽:某个业务缓存数据到期了本来只需要删除这个业务的数据就可以,但是同事把所有缓存数据都删除了,造成数据库宕机……有回复提到缓存击穿
和缓存雪崩
,于是就去查了资料,之前只是听说,也不知道具体是什么以及解决方法。
缓存击穿
概念
定义: 缓存击穿是指在高并发访问的情况下,某个非常热点的数据突然过期,导致大量请求在缓存中找不到数据,从而直接访问数据库,造成数据库压力瞬间增大。
发生原因:
- 热点数据过期。
- 高并发访问热点数据。
影响:
- 数据库承受大量突发请求,可能导致性能问题。
侧重点: 缓存击穿侧重于单个热点数据的缓存失效问题。
Redis示例: 假设有一个热点商品信息缓存在Redis中,设置了过期时间。当商品信息过期时,如果有大量用户同时访问该商品,由于缓存失效,这些请求会直接穿透到数据库。
总结
- 影响范围:缓存击穿主要影响单个热点数据。当这个热点数据在缓存中过期或不存在时,所有访问该数据的请求都会直接穿透到数据库,造成数据库的瞬时高负载。
- 场景:通常发生在单个非常热门的缓存项上,例如,一个热门商品的详细信息、一篇热门文章的内容等。
- 后果:虽然数据库压力增大,但通常仅限于特定的数据查询,影响相对有限。
解决方法
- 互斥锁:确保同一时间只有一个请求去数据库查询并更新缓存。
- 异步更新缓存:在缓存未命中后,异步地将数据库查询结果更新到缓存中。
- singlefilght:
singlefilght
是 Go 标准库的包,可以确保对于同一资源的多个并发请求,实际上只有一个请求会去执行数据库查询,其他请求会等待这个查询完成,然后共享结果 - 永不过期:为缓存数据设置较长的过期时间或永不过期,通过后台定时任务来更新缓存。
- 双重检查锁定:在互斥锁内部再次检查缓存,以避免不必要的数据库查询。
常用解决方法的优劣
互斥锁(Mutex)
性能
- 优点:在低并发情况下,性能开销较小。
- 缺点:在高并发情况下,可能会导致大量请求阻塞,等待获取锁,从而增加延迟。
其他方面
- 线程安全:可以保证同一时间只有一个请求执行数据库查询。
- 复杂度:实现相对简单,但需要正确处理锁的获取和释放,以避免死锁。
异步更新缓存
性能
- 优点:可以减少请求的等待时间,因为主请求不需要等待数据库查询完成。
- 缺点:可能会创建大量goroutines,特别是在高并发情况下,这可能导致资源浪费和管理开销。
其他方面
- 响应时间:主请求可以快速返回,但可能需要等待缓存更新完成才能获取最新数据。
- 实现复杂度:需要管理异步操作和可能的并发问题。
singleflight
性能
- 优点:在处理高并发请求时,可以显著减少对数据库的重复查询,从而减少数据库的压力和响应时间。
- 缺点:第一个请求需要等待数据库查询,所有后续请求也会因此等待,这可能会增加延迟。
其他方面
- 资源利用:相比于异步更新,
singleflight
可以减少goroutines的创建,因为它只执行一次数据库查询。 - 去重:自动处理了对同一资源的重复请求,减少了不必要的数据库查询。
- 实现复杂度:使用简单,只需调用
Do
方法,但需要理解其工作原理。
综合比较
- 性能:
- 互斥锁在高并发下可能导致性能瓶颈。
- 异步更新可以提高响应速度,但可能增加系统资源的使用。
singleflight
在减少数据库压力方面表现优秀,但可能会增加第一个请求的延迟。
- 资源使用:
- 互斥锁的资源使用相对较低。
- 异步更新可能需要更多的goroutines和内存。
singleflight
在资源使用上比异步更新更高效。
- 可靠性:
- 互斥锁和
singleflight
都可以提供较为可靠的保证,确保数据库不会被重复查询。 - 异步更新可能需要额外的逻辑来处理缓存更新失败的情况。
- 互斥锁和
- 适用场景:
- 互斥锁适用于并发量不是特别高的场景。
- 异步更新适用于对响应时间有较高要求的场景。
singleflight
适用于需要减少数据库重复查询的场景,特别是当数据库查询开销较大时。
在选择解决方案时,应根据具体场景和需求进行权衡。例如,如果数据库查询非常耗时且并发量高,singleflight
可能是更好的选择。如果并发量适中,且对延迟敏感,互斥锁或异步更新可能更合适。
缓存雪崩
概念
定义: 缓存雪崩是指缓存系统中的大量缓存数据在同一时间失效,导致大量请求直接访问数据库,造成数据库压力急剧上升,甚至导致数据库崩溃。
发生原因:
- 大量缓存设置了相同的过期时间。
- 缓存服务器出现问题(如Redis服务宕机)。
影响:
- 大量缓存同时失效,导致数据库压力剧增。
- 可能会导致整个系统的服务不可用。
侧重点: 缓存雪崩侧重于大量缓存同时失效的问题。
Redis示例: 如果Redis中缓存了大量数据,并且这些数据的过期时间都是相同的(例如,都在午夜12点过期),那么在过期时间到来时,会有大量数据同时失效,导致大量请求直接访问数据库。
总结
- 影响范围:缓存雪崩影响的是整个缓存系统。当大量缓存数据几乎同时过期,或者缓存服务本身出现故障时,所有依赖于缓存的请求都可能直接访问数据库,导致数据库压力急剧上升。
- 场景:可能发生在缓存数据的过期时间配置不当,或者缓存服务器出现故障时。
- 后果:由于影响的是所有缓存数据,可能导致整个系统范围内的服务不可用,影响范围广泛。
解决方法
- 避免相同过期时间:为缓存数据设置不同的过期时间,避免大量缓存同时失效。
- 缓存集群:使用Redis集群来提高缓存系统的可用性。
- 熔断、限流:实施熔断和限流机制,保护数据库不被大量请求压垮。
- 备份缓存:在主缓存失效时,使用备份缓存来提供服务。