redis缓存失效问题
redis数据失效导致的雪崩
因为缓存失效,从而导致大量的请求没有命中缓存,导致请求全部打到数据库。
1.大量请求,导致数据库处理不过来,整个系统依赖数据库的功能全部崩溃。
2.单系统挂掉,其它依赖于该系统的应用也会出现不稳定甚至崩溃。
redis数据失效的场景
1.因为打到内存阀值,采用数据淘汰策略(LRU/LFU)导致数据失效。
2.数据设置了过期时间,达到过期时间后,数据失效。
3.因为故障或者宕机/升级,导致服务重启,数据失效。
防止redis数据失效方法
1.保证内存充足,淘汰的数据都是非热点的数据,不会导致系统崩溃;可以采用redis集群分片部署。
2.对于大量的过期数据,设置过期错开,不要设置同一个过期时间,导致某一时刻,大量数据不命中。
3.做好数据备份,做好主从复制。
缓存雪崩解决方案
1.对数据库访问进行限流,保证不会因为缓存雪崩导致,数据库故障——使用信号量控制并发。
2.容错降级——返回指定的异常码。
代码实例:
// 数据库限流,根据数据库连接大小定义
Semaphore semaphore = new Semaphore(30);
public Object queryStock(String goodsId) {
String cacheKey = "goodsStock-" + goodsId;
// 1.先从Redis里面获取数据
String value = mainRedisTemplate.opsForValue().get(cacheKey);
// 2.缓存里面没有数据,从数据库取
if (value != null) {
logger.warn(Thread.currentThread().getName() + "缓存中获得数据+++++++++++++++++++++++++++");
return value;
}
// 3.去请求数据库,需要进行控制,根据连接数进行控制
try {
// 同一时间,只能有30个请求去数据库获取数据,并且重构缓存
boolean acquire = semaphore.tryAcquire(5, TimeUnit.SECONDS);
if (acquire) {
// 再次从Reids中获取数据
value = mainRedisTemplate.opsForValue().get(cacheKey);
if (value != null) {
logger.warn(" 缓存中获得数据+++++++++++++++++++++++++++");
return value;
}
value = databaseService.queryFromDatabase(goodsId);
System.err.println(" 数据库中获得数据==============================");
// 3.塞到缓存,过期时间
String v = value;
mainRedisTemplate.execute((RedisCallback<Boolean>) conn ->{
return conn.setEx(cacheKey.getBytes(), 120, v.getBytes());
});
} else {
// 等待时间
// 定义错误信息返回指定错误码
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放信号量
semaphore.release();
}
return value;
}
布隆过滤器——过滤不必要的查询
对于有些场景,当访问一个本就不存的数据时,这时我们不需要去走redis和数据库查询,应该直接返回一个错误的提示,这样可以防止恶意访问,导致服务器瘫痪。
布隆过滤器(Bloom Filter):是1970年布隆提出的,它实际上是一个很长的二进制数组和一系列hash函数,布隆过滤器可以用于检索一个元素是否在一个集合中;它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。
布隆过滤器的构建过程:
1.加载符合条件的记录
2.计算每条元素的hash值
3.计算hash值对应二进制数据的位置
4.将对应位置的值改为1
查找元素是否存在的过程:
1.计算元素的hash值
2.计算hash值对应二进制数组的位置
3.找到数组中对应的位置的值,0代表不存在,1代表存在。
代码实例:
@Service
// 构建一个布隆过滤器
public class RedisBloomFilter {
// redis连接信息
@Value("${spring.redis.main.hostName}")
private String redisHost;
@Value("${spring.redis.main.port}")
private int redisPort;
// bloomfilter 客户端
private Client client;
@PostConstruct
public void init() {
// bloomfilter 客户端
client = new Client(redisHost, redisPort);
}
/**
* 创建一个自定义的过滤器
* @param filterName
*/
public void createFilter(String filterName) {
// 创建一个容量10万,误判率0.01%的布隆过滤器
client.createFilter(filterName, 1000, 0.1);
}
/**
* 添加元素
* @param filterName
* @param value
* @return
*/
public boolean addElement(String filterName, String value) {
return client.add(filterName, value);
}
/**
* 判断元素是否存在
* @param filterName
* @param value
* @return
*/
public boolean exists(String filterName, String value) {
return client.exists(filterName, value);
}
}
public Object queryStock(final String goodsId) {
String cacheKey = "goodsStock-"+ goodsId;
// 增加一个布隆过滤器的筛选
boolean exists = filter.exists("goodsBloomFilter", cacheKey);
if(!exists) {
logger.warn(Thread.currentThread().getName()+" 您需要的商品不存在+++++++++++++++++++++++++++");
return "您需要的商品不存在";
}
public Object queryStock(String goodsId) {
String cacheKey = "goodsStock-" + goodsId;
// 1.先从Redis里面获取数据
String value = mainRedisTemplate.opsForValue().get(cacheKey);
// 2.缓存里面没有数据,从数据库取
if (value != null) {
logger.warn(Thread.currentThread().getName() + "缓存中获得数据+++++++++++++++++++++++++++");
return value;
}
// 3.去请求数据库,需要进行控制,根据连接数进行控制
try {
// 同一时间,只能有30个请求去数据库获取数据,并且重构缓存
boolean acquire = semaphore.tryAcquire(5, TimeUnit.SECONDS);
if (acquire) {
// 再次从Reids中获取数据
value = mainRedisTemplate.opsForValue().get(cacheKey);
if (value != null) {
logger.warn(" 缓存中获得数据+++++++++++++++++++++++++++");
return value;
}
value = databaseService.queryFromDatabase(goodsId);
System.err.println(" 数据库中获得数据==============================");
// 3.塞到缓存,过期时间
String v = value;
mainRedisTemplate.execute((RedisCallback<Boolean>) conn ->{
return conn.setEx(cacheKey.getBytes(), 120, v.getBytes());
});
} else {
// 等待时间
// 定义错误信息返回指定错误码
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放信号量
semaphore.release();
}
return value;
}
上一篇: java构造函数_Spring 自动装配模式之构造函数装配方式
下一篇: Netty—网络的高层建筑