当前位置:   article > 正文

【Redis】什么是Redis缓存 雪崩、穿透、击穿?(一篇文章就够了)_redis 雪崩

redis 雪崩

目录

什么是Redis?

Redis的正常存储流程?

什么是Redis缓存雪崩?

缓存雪崩

缓存预热

缓存失效时间的随机性

什么是Redis缓存穿透?

缓存穿透

缓存空对象

BloomFilter(布隆过滤器)

什么是Redis缓存击穿?

缓存击穿

互斥锁

逻辑过期时间


什么是Redis?

        Redis:是一种高性能开源的基于内存的,采用键值对存储的非关系型数据库,不保证数据的ACID特性【事务一旦提交,都不会进行回滚】

        采用键值对存储数据在内存或磁盘中,可以对关系型数据库起到补充作用,同时支持持久化[可以将数据保存在可掉电设备中],可以将数据同步保存到磁盘。

说Redis很快是相对于关系型数据库如mysql来说的,主要有以下因素

    第一,数据结构简单,所以速度快【采用键值对的方式】;

    第二,基于内存进行存储,不需要存储数据库,所以速度快;

    第三,采用多路IO复用模型,减少网络IO的时间消耗,避免大量的无用操作,所以速度快;

    第四,单线程避免了线程切换和上下文切换产生的消耗,所以速度快;

Redis的正常存储流程?

        正常流程:当用户访问服务端时,服务端会去访问Redis缓存中是否有该数据的缓存,如果有,就直接返回;如果没有,就去数据库进行查询;查询到结果过后直接返回给客户端,并且将查询的结果数据同步到redis缓存当中。

 

什么是Redis缓存雪崩?

缓存雪崩

        是指在某一时刻缓存中的存储的数据同时大量地失效,而且这些数据都是经常被访问的数据(比如热点文章,热门商品等),这样就会导致大量的请求都会落到数据库上,造成数据库的压力瞬间增大,从而导致服务器宕机,形成一种“雪崩”效应。

        简单来说,就是大量的redis同一时间大面积的失效,大量的请求直接打到数据库上(导致数据库压力飙升),这种现象就是缓存雪崩。

        解决缓存雪崩的方法有很多,其中一种比较常见的方法是采用“缓存预热”和“缓存失效时间的随机性”两种手段来解决。

缓存预热

        是指系统上线后,将相关的缓存数据直接加载到缓存中,这样一来,第一个请求过来的时候,就可以直接在缓存中获取到数据,而不需要去数据库中查询。

缓存失效时间的随机性

        是指在设置缓存的失效时间时,不能够将所有缓存的失效时间都设置为相同的值,而是要在一个合理的时间范围内进行随机,这样可以避免缓存在某一时刻大量失效的情况。

下面是一个采用了缓存预热和缓存失效时间的随机性的Java代码:

  1. public class UserService {
  2. private static final Random RANDOM = new Random();
  3. private RedisTemplate<String, User> redisTemplate;
  4. //可以将所有的初始化需要执行的代码放在一个单独的类中,使用@Scheduled或quartz或xxl-job定时控制执行
  5. public UserService(RedisTemplate<String, User> redisTemplate) {
  6. this.redisTemplate = redisTemplate;
  7. // 缓存预热,将所有用户数据加载到缓存中
  8. List<User> users = getUsersFromDB();
  9. for (User user : users) {
  10. redisTemplate.opsForValue().set(user.getId(), user, getExpireTime());
  11. }
  12. }
  13. public User getUser(String userId) {
  14. // 先从缓存中获取数据
  15. User user = redisTemplate.opsForValue().get(userId);
  16. if (user != null) {
  17. return user;
  18. }
  19. // 缓存中没有数据,则从数据库中查询
  20. user = getUserFromDB(userId);
  21. if (user != null) {
  22. // 将数据放入缓存,并且设置一个随机的失效时间
  23. redisTemplate.opsForValue().set(userId, user, getExpireTime());
  24. }
  25. return user;
  26. }
  27. private List<User> getUsersFromDB() {
  28. // 从数据库中查询所有用户数据
  29. // ...
  30. }
  31. private User getUserFromDB(String userId) {
  32. // 从数据库中查询用户数据
  33. // ...
  34. }
  35. private long getExpireTime() {
  36. // 在30分钟到1小时之间随机一个时间作为缓存的失效时间
  37. return 1800 + RANDOM.nextInt(1800);
  38. }
  39. }

 

什么是Redis缓存穿透?

缓存穿透

        是指恶意的请求,会故意查询数据库不存在的数据,而这些数据在Redis缓存中也不存在,这样就会导致大量的请求都会落到数据库上,造成数据库的压力瞬间增大,从而导致服务器宕机。

解决缓存穿透的方法有很多,其中一种比较常见的方法是采用“缓存空对象”和“BloomFilter(布隆过滤器)两种手段来解决。

缓存空对象

        是指在查询数据库时,如果发现查询的数据不存在,那么就将这个空对象也缓存起来,这样一来,下次再查询这个不存在的数据时,就可以直接在缓存中获取到空对象,而不需要去数据库中查询。

BloomFilter(布隆过滤器)

        【Redis】布隆过滤器_布隆过滤器需要保存-CSDN博客

        是一种概率性数据结构,可以用于判断一个元素是否存在于一个集合中,它的优势在于空间复杂度低、查询速度快。

下面是一个采用了缓存空对象和BloomFilter的Java代码:

  1. public class UserService {
  2. private RedisTemplate<String, User> redisTemplate;
  3. private BloomFilter<String> bloomFilter;
  4. public UserService(RedisTemplate<String, User> redisTemplate, BloomFilter<String> bloomFilter) {
  5. this.redisTemplate = redisTemplate;
  6. this.bloomFilter = bloomFilter;
  7. // 将所有用户的ID都放入BloomFilter中
  8. List<User> users = getUsersFromDB();
  9. for (User user : users) {
  10. bloomFilter.add(user.getId());
  11. }
  12. }
  13. public User getUser(String userId) {
  14. // 先判断BloomFilter中是否存在该ID
  15. if (!bloomFilter.mightContain(userId)) {
  16. return null;
  17. }
  18. // 再从缓存中获取数据
  19. User user = redisTemplate.opsForValue().get(userId);
  20. if (user != null) {
  21. return user;
  22. }
  23. // 缓存中没有数据,则从数据库中查询
  24. user = getUserFromDB(userId);
  25. if (user != null) {
  26. // 将数据放入缓存
  27. redisTemplate.opsForValue().set(userId, user, 3600);
  28. } else {
  29. // 将空对象放入缓存
  30. redisTemplate.opsForValue().set(userId, null, 300);
  31. }
  32. return user;
  33. }
  34. private List<User> getUsersFromDB() {
  35. // 从数据库中查询所有用户数据
  36. // ...
  37. }
  38. private User getUserFromDB(String userId) {
  39. // 从数据库中查询用户数据
  40. // ...
  41. }
  42. }
  43. ``

 

什么是Redis缓存击穿?

缓存击穿

        是指某个热点数据在缓存中失效了,而且这个数据也是经常被访问的数据,这样就会导致大量的请求都会落到数据库上,造成数据库的压力瞬间增大,从而导致服务器宕机。

解决缓存击穿的方法有很多,其中一种比较常见的方法是采用“互斥锁”和“逻辑过期时间”两种手段来解决。

互斥锁

        是指在查询数据库时,如果发现缓存中的数据已经失效,那么就先获取一个互斥锁,然后再去查询数据库,这样一来,只有一个线程去访问数据库,将查询到的数据同步到缓存当中,其他线程就直接从缓存中获取了,就可以避免大量的请求都会落到数据库上。

逻辑过期时间

        是指在查询数据库时,如果发现缓存中的数据已经失效,那么就先将数据库中的数据缓存起来,但是不将这个数据的过期时间设置为实际的过期时间,而是将过期时间设置为一个较短的时间,这样一来,就可以避免缓存中的数据一直都是“冷”数据。

下面是一个采用了互斥锁和逻辑过期时间的Java代码:

  1. public class UserService {
  2. private RedisTemplate<String, User> redisTemplate;
  3. private RedissonClient redissonClient;
  4. public UserService(RedisTemplate<String, User> redisTemplate, RedissonClient redissonClient) {
  5. this.redisTemplate = redisTemplate;
  6. this.redissonClient = redissonClient;
  7. }
  8. public User getUser(String userId) {
  9. // 先从缓存中获取数据
  10. User user = redisTemplate.opsForValue().get(userId);
  11. if (user != null && !user.isExpired()) {
  12. return user;
  13. }
  14. // 获取互斥锁
  15. RLock lock = redissonClient.getLock("user:" + userId);
  16. try {
  17. if (lock.tryLock(10, 5, TimeUnit.SECONDS)) {
  18. // 再次从缓存中获取数据
  19. user = redisTemplate.opsForValue().get(userId);
  20. if (user != null && !user.isExpired()) {
  21. return user;
  22. }
  23. // 从数据库中查询数据
  24. user = getUserFromDB(userId);
  25. if (user != null) {
  26. // 将数据放入缓存,并且设置一个逻辑过期时间
  27. redisTemplate.opsForValue().set(userId, user, 3600);
  28. user.setExpireTime(System.currentTimeMillis() + 300000);
  29. }
  30. }
  31. } finally {
  32. // 释放互斥锁
  33. if (lock != null && lock.isHeldByCurrentThread()) {
  34. lock.unlock();
  35. }
  36. }
  37. return user;
  38. }
  39. private User getUserFromDB(String userId) {
  40. // 从数据库中查询用户数据
  41. // ...
  42. }
  43. }
  44. public class User {
  45. private String id;
  46. private String name;
  47. private long expireTime;
  48. public User(String id, String name) {
  49. this.id = id;
  50. this.name = name;
  51. this.expireTime = System.currentTimeMillis() + 300000;
  52. }
  53. public String getId() {
  54. return id;
  55. }
  56. public String getName() {
  57. return name;
  58. }
  59. public boolean isExpired() {
  60. return System.currentTimeMillis() > expireTime;
  61. }
  62. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/915993
推荐阅读
相关标签
  

闽ICP备14008679号