缓存雪崩
产生原因
- redis缓存的key,在同一时间大量失效,导致大量请求直接打到数据库,造成数据库挂掉
- redis宕机
解决办法
- 设置小随机失效时间,给这些数据过期时间增加一些较小的随机数,不同数据的过期时间有差别,但是差别又不大
- 服务降级
缓存击穿
产生原因
要访问的数据既不是Redis缓存中也不再数据库中,导致请求在访问缓存时,发生缓存缺失,再去访问数据库时,发现数据库中也没有要访问的数据。
如果应用持续有大量请求访问数据,就会同时给缓存和数据库带来巨大压力。
业务层误操作:缓存中的数据和数据库中的数据都被误删除,所以缓存和数据库中都没有数据;
恶意攻击:专门访问数据库中没有的数据
解决办法
- 缓存空值或者缺省值
一旦发生缓存穿透,针对查询数据在Redis缓存一个空值或者业务层协商确定的缺省值。应用发生后续请求在进行查询时,可以直接从Redis读取空值或者缺省值返回给业务应用,避免大量请求发送给数据库处理,保持数据库的正常运行。 - 使用布隆过滤器快速判断数据是否存在,避免从数据库中查询数据是否存在,减轻数据库压力。
- 对请求参数进行校验,避免请求进入缓存或者数据库。
缓存穿透
产生原因
热点key失效,大量请求发送到数据库,导致数据库压力激增影响数据库处理其他请求。
解决办法
- 对于访问特别频繁的热点数据,不设置过期时间。
- 对于热点数据的请求访问,都在缓存中进行处理。
- 数据库锁,并设置延时,重新查询redis
布隆过滤器
https://www.bilibili.com/video/BV11M4y157uK
package com.uuorb.xueersi.util;
import org.springframework.stereotype.Component;
import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.BitSet;
@Component
public class BloomUtil {
private static final BloomUtil _instance;
/**
* 位数组大小
*/
private static final int DEFAULT_SIZE = 2 << 24;
/**
* 通过这个数组创建多个Hash函数
*/
private static final int[] SEEDS = new int[]{6, 18, 64, 89};
/**
* 初始化位数组,数组中的元素只能是 0 或者 1
*/
private BitSet bits = new BitSet(DEFAULT_SIZE);
/**
* @return
*/
public static BloomUtil getInstance() {
return _instance;
}
/**
* Hash函数数组
*/
private MyHash[] myHashes = new MyHash[SEEDS.length];
/**
* 添加元素到位数组
*/
public void add(Object value) {
for (MyHash myHash : myHashes) {
bits.set(myHash.hash(value), true);
}
}
/**
* 判断指定元素是否存在于位数组
*/
public boolean contains(Object value) {
boolean result = true;
for (MyHash myHash : myHashes) {
result = result && bits.get(myHash.hash(value));
}
return result;
}
public BloomUtil() {
// 初始化多个不同的 Hash 函数
for (int i = 0; i < SEEDS.length; i++) {
myHashes[i] = new MyHash(DEFAULT_SIZE, SEEDS[i]);
}
}
static {
_instance = new BloomUtil();
}
/**
* 自定义 Hash 函数
*/
private class MyHash {
private int cap;
private int seed;
MyHash(int cap, int seed) {
this.cap = cap;
this.seed = seed;
}
/**
* 计算 Hash 值
*/
final int hash(Object obj) {
return (obj == null) ? 0 : Math.abs(seed * (cap - 1) & (obj.hashCode() ^ (obj.hashCode() >>> 16)));
}
}
}