1.缓存穿透
是指缓存和数据库都没有的数据,而用户不断发起请求,导致数据库压力过大。
解决方案:
- 对空值缓存。如果一个查询返回的数据为空(不管数据是否存不存在),仍将这个空结果进行缓存。设置空结果的过期时间很短,最长不超过5分钟。
- 设置可访问名单。使用bitmaps类型定义一个可访问名单。名单id作为bitmaps的偏移量,每次访问和bitmap里面的id进行比较。如果访问id不在bitmaps里,进行拦截,不允许访问。
- 采用布隆过滤器。
- 设置黑名单。
2.缓存击穿
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
解决方案:
- 预先设置热门数据。在redis高峰访问之前,把一些热门数据提前存入到redis里面,加大这些热门数据key的时长。
- 实时调整。调整key的过期时间。
- 使用锁。
3.缓存雪崩
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方案:
- 构建多级缓存架构。
- 使用锁或者队列。
- 设置过期标志更新缓存。
- 将缓存失效时间分散开。
4.分布式锁
- 跨服务之间上锁,普通的java锁无法处理。将redis中的key当成锁,key不存在表示未上锁,key存在表示上锁。
- 使用setnx加上redis本身是单线程保证并发情况下的线程安全,ex设置过期时间防止死锁。
- 给key的value设置不同的uuid,每个请求只能释放自己对于uuid的锁。
- 释放锁分为两步,一步判断uuid是否是自己,一步是释放锁。这两步需要用lua脚本来保证原子性。