如果redis宕机了,或者链接不上,怎么办?
如果redis缓存在高峰期到期失效,在这个时刻请求会向雪崩一样,直接访问数据库如何处理?
如果用户不停地查询一条不存在的数据,缓存没有,数据库也没有,那么会出现什么情况,如何处理?
1、缓存穿透
- 是指查询一个一定不存在的数据,由于缓存是不命中,将去查询数据库,但是数据库也无此记录,这导致这个不存在的数据每次请求都要到数据库查询,失去了缓存的意义。这是利用redis和mysql的机制(redis缓存一旦不存在,就访问mysql),直接绕过缓存访问mysql,而制造的db请求压力
- 解决
空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
jedis.setex("sku:" + skuId + ":info",60 * 3, JSON.toJSONString(""));
2、缓存雪崩
- 缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。
- 解决:
原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
Random r = new Random();
int time = r.nextInt(5) + 1;
jedis.setex("sku:" + skuId + ":info",time,JSON.toJSONString(pmsSkuInfo));
3、缓存击穿
- 是某一个热点key在高并发访问的情况下,突然失效,导致大量的并发打进mysql数据库的情况
- 解决
使用分布式锁
-
第一种分布式锁:redis自带的分布式锁
nx:只有在该key不存在时才能成功String token = UUID.randomUUID().toString(); String OK = jedis.set("sku:" + skuId + ":lock",token,"nx","px",10*1000); if (StringUtils.isNotBlank(OK)&&OK.equals("OK")) { //设置成功,可以在过期时间内访问数据库 }else { //设置失败,自旋(该线程在睡眠几秒后,重新尝试访问本方法) try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } return 本方法名; }
- 如果在redis中的锁已经过期了,然后锁过期的那个请求又执行完毕,回来删锁,删除了其他线程的锁,怎么办?
//用token确认是否是自己的锁 String lockToken = jedis.get("sku:" + skuId + ":lock"); if (StringUtils.isNotBlank(lockToken)&&lockToken.equals(token)) { //在访问mysql后,将mysql的分布锁释放 jedis.del("sku:" + skuId + ":lock"); }
- 如果碰巧在查询redis锁还没删除的时候,正在网络传输时,锁过期了怎么办?
//为防止在删除锁的前一瞬间锁过期,使用lua脚本使得在确认锁存在时瞬间删掉锁 String script ="if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; jedis.eval(script, Collections.singletonList("lock"),Collections.singletonList(token));
-
第二种分布式锁:redisson框架,一个redis的带有juc的lock功能的客户端的实现(既有jedis的功能,又有juc的锁功能)
-