1.缓存在高并发和安全压力下的一些问题
缓存击穿:
是某一个热点key在高并发访问的情况下,突然失效,导致大量的并发打进mysql数据库的情况
缓存穿透:
是利用redis和mysql的机制(redis缓存一旦不存在,就访问mysql),直接绕过缓存访问mysql,而制造的db请求压力
一般在代码中防止该现象的发生
解决:// 为了防止缓存穿透将,null或者空字符串值设置给redis
缓存雪崩:
缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,导致的db崩溃
解决:设置不同的缓存失效时间
2.如何解决缓存击穿的问题
穿透:利用不存在的key去攻击mysql数据库
雪崩:缓存中的很多key失效,导致数据库负载过重宕机
击穿:在正常的访问情况下,如果缓存失效,如果保护mysql,重启缓存的过程
使用redis数据库的分布式锁,解决mysql的访问压力问题
第一种分布式锁:redis自带一个分布式锁,set px nx
redis分布式锁案例:
@Override
public Food getFoodById(String id) {
Food food= new Food ();
// 链接缓存
Jedis jedis = redisUtil.getJedis();
// 查询缓存(缓存到redis的key设计是:food:id:info)
String foodKey = "food:"+id+":info";
String foodJson = jedis.get(foodKey);
if(StringUtils.isNotBlank(skuJson)){
System.out.println(Thread.currentThread().getName()+"从缓存中获取数据");
food= JSON.parseObject(foodJson, Food.class);
}else{
// 如果缓存中没有,查询mysql
System.out.println("发现缓存中没有,申请缓存的分布式锁:"+"food:" + id+ ":lock");
// 设置分布式锁
String token = UUID.randomUUID().toString();
String OK = jedis.set("food:" + id+ ":lock", token, "nx", "px", 10*1000);// 拿到锁的线程有10秒的过期时间
if(StringUtils.isNotBlank(OK)&&OK.equals("OK")){
// 设置成功,有权在10秒的过期时间内访问数据库
System.out.println(Thread.currentThread().getName()+"有权在10秒的过期时间内访问数据库:"+"food:" + id+ ":lock");
food= getFoodByIdFromDb(id);//调用查询方法
if(food!=null){
// mysql查询结果存入redis
jedis.set("food:"+id+":info",JSON.toJSONString(food));
}else{
// 数据库中不存在该数据
// 为了防止缓存穿透将,null或者空字符串值设置给redis
jedis.setex("food:"+id+":info",60*3,JSON.toJSONString(""));
}
// 在访问mysql后,将mysql的分布锁释放
System.out.println(Thread.currentThread().getName()+"使用完毕,将锁归还:"+"food:" + id+ ":lock");
String lockToken = jedis.get("food:" + id+ ":lock");
if(StringUtils.isNotBlank(lockToken)&&lockToken.equals(token)){
jedis.del("food:" + id+ ":lock");// 用token确认删除的是自己的数据的锁
}
}else{
// 设置失败,自旋(该线程在睡眠几秒后,重新尝试访问本方法)
System.out.println(Thread.currentThread().getName()+"没有拿到锁,开始自旋");
return getFoodById(id);
}
}
jedis.close();
return food;
}