缓存系统的三大问题

一、缓存穿透

  • 数据库和缓存中都没有数据,用户每次查询都查询数据库但是没法写入到缓存
  • 查询的KEY不存在,例如id:-1

解决方法:

  • 过滤不正常的参数key
  • 将查询空的结果设置短时间的key-null缓存

二、缓存击穿

  • 数据库中有数据,但是缓存没有,首次请求或者是缓存的KEY过期

解决方法:

  • 限流、熔断、降级
  • key不过期(注意使用)
  • 佈隆过滤器,快速查询key是否存在
  • 互斥锁(常用)

三、缓存雪崩

  • 缓存中大批量数据过期

解决方法:

  • 缓存数据随机时间
  • 分散缓存到不同缓存系统
  • key不过期
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* @param key 缓存的key
* @param loader lambda方法,通过这个方法获取数据
*/
public T get(String key, Callable<? extends T> loader) {
String cache = redisHelper.get(key);
if (StringUtils.isNotBlank(cache)) {
if ("null".equals(cache)) {
return null;
}
return JSONObject.parseObject(cache, this.entityClass);
}
String lockKey = "lock:" + key;
String lockVal = String.valueOf(RandomUtils.nextInt(1000, 9999));
//通过加锁防止击穿
if (redisHelper.lock(lockKey, lockVal, 3, TimeUnit.SECONDS)) {
// 通过回调方式调用获取数据
T entity = loader.call();
if (entity != null) {
redisHelper.set(key, JsonHelper.toJson(entity), RandomUtils.nextInt(3600, 7200));
} else {
//防止缓存穿透
redisHelper.set(key, "null", 10);
}
redisHelper.unlock(lockKey, lockVal);
return entity;
} else {
TimeUnit.MILLISECONDS.sleep(RandomUtils.nextInt(100, 200));
return this.get(key, loader);
}
return null;
}