点评的缓存问题
本文最后更新于 2025年4月18日 晚上
目前处于烂尾状态。。。我很抱歉。。
缓存更新

- 等redis内存满了自己淘汰
- 超时
- 主动更新
主动更新
- 更新数据库时更新缓存
- 缓存与数据库整合
- 调用者只操作缓存,由其他线程异步将缓存持久化到数据库中,保证最终一致
先删缓存还是先操作数据库?
先操作数据库,再更新缓存。
缓存操作比数据库操作快;数据库读比写快:所以先删缓存,在数据库update完成前,可能会导致期间的查询不一致。
缓存穿透
客户端的请求在缓存和数据库中都不存在,缓存永远不会生效,请求都会打到数据库上,次次查询数据库然后返回不存在。
解决方法
- 布隆过滤器
- 不存在就一定不存在,存在可能会误判
- 快速
- 缓存空对象
- 实现简单,但是内存消耗且可能不一致
- 需要给空对象设置TTL(Time To Live)
缓存雪崩
同一时段,大量缓存的有效期到期失效,或者redis宕机,导致大量请求短期内集中打到数据库。
解决方法
- TTL随机:N+n模式,n随机
- 集群
- 多级缓存
缓存击穿
热点key问题,一个被高并发访问并且缓存重建复杂的key突然失效,在瞬间给数据库带来巨大压力。
解决方法
- 互斥锁
- 逻辑过期

互斥锁业务流程

逻辑过期业务流程

逻辑过期需要缓存预热:
注意到逻辑过期如果一开始缓存未命中,就会直接空。
因为是防止打到数据库,所以也不会因为缓存未命中而去查数据库。
所以需要缓存预热,一开始的缓存中需要存在所需的数据,否则就会一直缓存击穿未遂而返回空对象。
缓存预热
黑马的视频里没有,自己试着加了一个,与逻辑过期一起使用。
由于缓存预热的数据具有特殊性,所以不写在CacheClient这样的通用工具类中,而是写在ShopServiceImpl类里。
写好缓存预热方法如下,逻辑很简单,设置一个缓存标记,如果没有标记,证明缓存不存在,则预热:
1 | |
然后新建一个CacheWarmupRunner类,需要实现ApplicationRunner接口,加上@Component注解,然后注入ShopService,在类里面重写run方法,在run里调用warmupCache。
ApplicationRunner和CommendLineRunner:CommandLineRunner 和 ApplicationRunner 都是 Spring Boot 应用程序启动后要执行的接口,它们都允许我们在应用启动后执行一些自定义的初始化逻辑,例如缓存预热。
CommandLineRunner和ApplicationRunner区别如下:方法签名不同
CommandLineRunner接口有一个run(String... args)方法,它接收命令行参数作为可变长度字符串数组。ApplicationRunner接口则提供了一个run(ApplicationArguments args)方法,它接收一个ApplicationArguments对象作为参数,这个对象提供了对传入的所有命令行参数(包括选项和非选项参数)的访问。参数解析方式不同
CommandLineRunner接口更简单直接,适合处理简单的命令行参数。ApplicationRunner接口提供了一种更强大的参数解析能力,可以通过ApplicationArguments获取详细的参数信息,比如获取选项参数及其值、非选项参数列表以及查询是否存在特定参数等。使用场景不同
- 当只需要处理一组简单的命令行参数时,可以使用
CommandLineRunner。- 对于需要精细控制和解析命令行参数的复杂场景,推荐使用
ApplicationRunner。