在高并发下,查询一个不存在的值时,缓存不会被命中,导致大量请求直接落到数据库上,如活动系统里面查询一个不存在的活动。
解决方案:
布隆过滤器。首先也是对所有可能查询的参数以hash形式存储,当用户想要查询的时候,使用布隆过滤器发现不在集合中,就直接丢弃,不再对持久层查询。缓存空对象。当持久层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源。缺点是:占用更多的内存。在高并发下,大量的缓存key在同一时间失效,导致大量的请求落到数据库上,如活动系统里面同时进行着非常多的活动,但是在某个时间点所有的活动缓存全部过期。
解决方案:
给缓存的失效时间,加上一个随机值,避免集体失效。两级缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作。然后细分以下几个小点 ● I 先从缓存A读数据,有则直接返回; ● II A没有数据,直接从B读数据,直接返回,并且异步启动一个更新线程; ● III 更新线程同时更新缓存A和缓存B。一个key非常热点,在不停的扛着高并发,高并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的高并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
解决方案:
使用互斥锁(mutex key):同一时刻只有一个client能够获取数据。永不过期:没有设置过期时间就保证不会出现热点key过期问题多个client线程同时set key引起的并发问题
解决方案:
分布式锁+时间戳:准备一个分布式锁(Redis自己有实现),并发访问哪个资源,就将该资源作为key,值为一个唯一的随机值(如unix时间戳加上ip之类的),这个key在Redis中存在,表示对此资源上锁了,用完再把这个key删了就行。消息队列:把Redis.set操作放在队列中使其串行化,必须的一个一个执行。这种方式在一些高并发的场景中算是一种通用的解决方案。保证不了,就是丢了。
[1] redis缓存与数据一致性
[2] 缓存穿透、缓存击穿和缓存雪崩实践