更多内容,欢迎关注微信公众号:全菜工程师小辉~
缓存穿透缓存系统,一般流程都是按照key去查询缓存,如果不存在对应的value,就去后端系统(例如:持久层数据库)查找 。如果key对应的value是一定不存在的,并且对该key并发请求量很大,就会对后端系统造成很大的压力,这就叫做缓存穿透 。
正常请求:
文章插图
正常请求
缓存击穿时:
文章插图
缓存击穿
如何避免
1. 缓存空结果
对查询结果为空的情况进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存 。
2. 布隆过滤器
采用布隆过滤器,guava有实现api,或者使用redis的bitmap 。将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力 。布隆过滤器对于固定的数据可以起到很好的效果,但是对于频繁更新的数据,布隆过滤器的构建会面临很多问题 。另外布隆过滤器是有判断误差的,网上有很多详细的介绍,请读者自行搜索即可 。
文章插图
布隆过滤器
缓存雪崩当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力 。
如何避免
1. 互斥锁
在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量 。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待 。
文章插图
互斥锁
如果是单机,可以用synchronized或者lock来处理,如果是分布式环境就需要使用分布式锁 。
使用互斥锁,代码如下,仅适用redis2.6.1以后支持setnx的版本 。在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用redis的setnx操作去set一个mutex key 。当操作返回成功时,再进行load db的操作并回设缓存,否则,就重试整个get缓存的方法 。
文章插图
互斥锁
public String get(key) { List<String> resultList = (List<String>)redisTemplate.opsForValue().get(key); if(CollectionUtils.isEmpty(resultList)){ final String mutexKey = key + "_lock"; boolean isLock = (Boolean) redisTemplate.execute(new RedisCallback() { @Override public Object doInRedis(RedisConnection connection) throws DataAccessException { //只在键key不存在的情况下,将键key的值设置为value,若键key已经存在,则 SETNX 命令不做任何动作 //命令在设置成功时返回 1 , 设置失败时返回 0 return connection.setNX(mutexKey.getBytes(),"1".getBytes()); } }); if(isLock){ //设置成1秒过期 redisTemplate.expire(mutexKey, 1000, TimeUnit.MILLISECONDS); resultList = getValueBySql(key); redisTemplate.opsForValue().set(key, resultList, 1000, TimeUnit.SECONDS); redisTemplate.delete(mutexKey); }else{ //线程休息50毫秒后重试 Thread.sleep(50); retryCount--; System.out.println("=====进行重试,当前次数:" + retryCount); if(retryCount == 0){ System.out.println("====这里发邮件或者记录下获取不到数据的日志,并为key设置一个空置防止重复获取"); List<String> list = Lists.newArrayList("no find"); redisTemplate.opsForValue().set(key, list, 1000, TimeUnit.SECONDS); return list; } return getCacheSave2(key,retryCount); } } return resultList;}2. 设置随机过期时间
不同的key,设置不同的过期时间,让缓存失效时间分散开,比如可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低 。
3. 设置二级缓存
做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期
4. “永远不过期”
“永远不过期”包含两层意思:
- 从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期 。
- 从功能上看,把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期 。
文章插图
推荐阅读
- 缓存算法FIFO、LFU、LRU
- 喝菊花好还是胎菊好,金丝皇菊什么样的好金丝皇菊和胎菊哪个好你了解吗
- 酒精进入人体的方式 如何将酒精快速排到体外
- 面部神经炎
- 如何快速减后背呢?
- 开发人员如何快速定制化实现一个基于Solr的搜索引擎
- 如何快速去水印批量搬运短视频,教你玩转自媒体短视频
- 怎样快速瘦身20斤小妙招,怎么快速瘦身10斤
- 爽肤水的功效作用你了解吗
- JAVA算法合集:冒泡+插入+快速+希尔+归并+桶+基数+剪枝+回溯算法