缓存击穿
名词解释:当我们缓存key设置过期时间,恰巧在这一刻这个key在某一刻被高并发的访问,把所有的请求都打到了DB中这就可能会导致DB挂了。这个跟后面说的缓存雪崩非常相似,这个和缓存雪崩的区别在于这里针对某一key缓存,但是雪崩则指的是多个key,要解决方案有很多,比如让一个线程构建缓存,另外线程等待知道构建好,或者redis维护timeout字段逻辑失效等等
String get(final String key) { T t = redis.get(key); String value = t.getValue(); long timeout = t.getTimeout(); // 当逻辑的超时时间到了时,异步构建缓存 if (timeout <= System.currentTimeMillis()) { threadPool.execute(new Runnable() { public void run() { String brokenKey = "broken_"+key; if (redis.setnx(brokenKey, "1")) { //redis加锁 更新过期时间 redis.expire(brokenKey, 60); //防止产生key不失效 String dbValue = db.get(key); redis.set(key, dbValue); redis.del(brokenKey); } } }); } return value; }
缓存穿透:看了上面介绍的缓存击穿了,现在怎么又出现了一个缓存穿透呢,感觉字面上穿透和击穿应该是一个意思啊,确实个人理解翻译上是差不多,但是对于redis的专业技术语上却是不同,缓存穿透指的是一些恶意人攻击,比如说登录的时候它请求一个一定不存在的用户名时,那么我们服务端正常应该是这样处理,首先到缓存中查询,没有在到数据查询,看看好像是没问题啊,查询一下应该很快,如果是这一刻恶意攻击发起几百万次请求,所以就会一直循环这个操作,一定不存在的用户名会一直查询数据库,那么你的数据库和缓存系统会不会挂呢?
方法1、布隆过滤器处理,把key放到布隆过滤器中,获取时查看是否存在,如果存在则获取缓存、获取数据库(把key放入缓存又2种方案,1、业务系统初始化 2、缓存中获取不到时单条插入到布隆过滤器)
优点: 高性能
缺点:布隆过滤器不支持删除,需要单独维护一个缓存key的集合, 另外布隆过滤器是有一定误差的。
方法2、也可以采用一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,然后但它的过期时间会很短,最长不超过五分钟。
缓存空对象会有两个问题:
第一,空值做了缓存,意味着缓存层中存了更多的键,需要更多的内存空间 ( 如果是攻击,问题更严重 ),比较有效的方法是针对这类数据设置一个较短的过期时间,让其自动剔除。
第二,缓存层和存储层的数据会有一段时间窗口的不一致,可能会对业务有一定影响。例如过期时间设置为 5分钟,如果此时存储层添加了这个数据,那此段时间就会出现缓存层和存储层数据的不一致,此时可以利用消息系统或者其他方式清除掉缓存层中的空对象。
缓存雪崩
如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。
这个没有完美解决办法,但可以分析用户行为,尽量让失效时间点均匀分布。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。
详细见
https://blog.csdn.net/lby0307/article/details/79680326