首先看一下数据的访问流程:
缓存穿透
概念:当用户使用一个不存在的key进行查询时,缓存(redis)无法命中,需要访问数据库查询该数据,若数据库中没有数据,则不写入缓存(redis)中,这将导致不存在的数据每次请求都要去数据库查询,造成缓存穿透。
解决办法:
- 使用布隆过滤器,通过bloomfilter.mightContain(key)来判断当前key是否命中。布隆过滤器原理:https://zhuanlan.zhihu.com/p/43263751
- 当数据库查询结果为空时,在redis中将key对应的value设置空值,并设置一个较短的过期时间。
缓存击穿
概念:当一个存在的key,在其缓存过期的那一刻,有大量的访问请求,这些请求将击穿缓存,访问数据库,造成数据库瞬时的大量请求。
解决办法:
- 使用互斥锁:在一个线程根据key获取value为空时,先加锁,再从数据库获取,并写入缓存后释放锁。其他线程在尝试获取锁失败时,睡眠线程50ms后重试。单机环境使用synchronized或者lock来处理,分布式环境可使用分布式锁(redis的setnx,zookeeper的增加节点操作等)
- 异步构建缓存:从线程池中获取线程异步构建缓存。该方案需要redis维护一个timeout,当timeout小于System.currentTimeMillis()时,则进行缓存更新,否则返回value
- 使用布隆过滤器