高并发下的缓存失效问题
缓存击穿
- 对于一些设置了过期时间的 key, 如果这些 key 可能还在某些时间点被超高并发地访问,是一种非常热点的数据(比如: 某某明星吃瓜事件等)
- 如果这个 key 在大量请求进来时正好失效,那么这些 key 就会穿过缓存区访问数据库
- 解决方法:加并发锁,在大量并发请求下,只让一个人去查,而其他人进行等待,查到之后释放锁,其他人再去获取锁,先查缓存,就会有数据,不用去 db
- 在单体应用中加锁,使用 synchronized
sychronized(this){ String catalogJSON = redisTemplate.opsForValue().get("catalogJSON"); if(!StringUtils.isEmpty(catalogJSON)){ // 加锁后,再次查询缓存 // 如果缓存不为 null, 直接返回 Map> result = JSON.parseObject(catalogJSON, new TypeReference
缓存穿透
- 在缓存中查询一个不存在的数据,由于缓存没有被命中,就会去查询数据库,数据库中也不存在此条记录,那么每次的请求都会越过缓存区查询数据库,缓存就失去了作为缓存的意义。
- 带来的问题:
- 当请求并发量很大的时候,数据库的压力增大的,最终可能导致崩溃
- 解决方法:
- 将该查询的key ,给它一个 value 为 null 的值,并加入短暂的过期时间,以防后面数据库有该值了,这个数据一直占着。
-
// 从缓存中获取数据 String catalogJson = redisTemplate.opsForValue().get("catalogJSON"); // 判断数据是否是空的 if(StringUtils.isEmpty(catalogJSON)){ // 如果为空,那么查询数据库 Map> catalogJsonFromDb = getCatalogJsonFromDb(); // 将查到的数据再放入缓存,将对象转为 json 放在缓存中 String s = JSON.toJSONString(catalogJsonFromDb); redisTemplate.opsForValue().set("catalogJSON", 0); return catalogJsonFromDb; }
缓存雪崩
- 设置缓存时 key 采用相同的过期时间,导致缓存在某一时刻同时失效,请求因此进入数据库,导致数据库压力过大。
- 解决方法: 设置的过期时间尽量不要一样,可以在一个基础的失效时间上增加一个随机值(1-5 分钟左右), 这样可以有效避免缓存大量失效。
-
// 从缓存中获取数据 String catalogJson = redisTemplate.opsForValue().get("catalogJSON"); // 判断数据是否是空的 if(StringUtils.isEmpty(catalogJSON)){ // 如果为空,那么查询数据库 Map
> catalogJsonFromDb = getCatalogJsonFromDb(); // 将查到的数据再放入缓存,将对象转为 json 放在缓存中 String s = JSON.toJSONString(catalogJsonFromDb); redisTemplate.opsForValue().set("catalogJSON", s, 1, TimeUnit,DAYS); // 时间按实际需要定义 return catalogJsonFromDb; }