Java模拟并解决缓存穿透问题

什么叫做缓存穿透

缓存穿透只会发生在高并发的时候,就是当有10000个并发进行查询数据的时候,我们一般都会先去redis里面查询进行数据,但是如果redis里面没有这个数据的时候,那么这10000个并发里面就会有很大一部分并发会一下子都去mysql数据库里面进行查询了

解决缓存穿透

首先我模拟一下缓存穿透

比如下面的代码

Pom.xml代码

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>springboot</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>
  <name>springboot</name>
  <description>Demo project for Spring Boot</description>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.1.RELEASE</version>
    <relativePath></relativePath> <!-- lookup parent from repository -->
  </parent>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>1.1.1</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>
Application.properties
server.port=8081
#DB Configuration:
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://47.91.248.236:3306/hello?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
#spring集成Mybatis环境
#pojo别名扫描包
mybatis.type-aliases-package=com.itheima.domain
#加载Mybatis映射文件
mybatis.mapper-locations=classpath:mapper/*Mapper.xml
MyController代码,下面的蓝色代码是模仿10000个并发线程
/**
 * sinture.com Inc.
 * Copyright (c) 2016-2018 All Rights Reserved.
 */
package com.itheima.controller;
import com.itheima.mapper.UserMapper;
import com.itheima.domain.User;
import com.itheima.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * @author xinzhu
 * @version Id: MyController.java, v 0.1 2018年12月05日 下午6:29 xinzhu Exp $
 */
@RestController
public class MyController {
  @Autowired
  private UserService userService;
  @RequestMapping("/hello/{id}")
  @ResponseBody
  public User queryUser(@PathVariable Integer id){
    // 蓝色代码注释开始
    new Thread(){
      @Override
      public void run() {
        for (int x = 0; x < 10000; x++) {
          userService.queryUser(id);
        }
      }
    }.start();
    // 蓝色代码注释结束
    return userService.queryUser(id);
  }
}

User类

/**
 * sinture.com Inc.
 * Copyright (c) 2016-2018 All Rights Reserved.
 */
package com.itheima.domain;
/**
 * @author xinzhu
 * @version Id: User.java, v 0.1 2018年12月06日 下午1:40 xinzhu Exp $
 */
public class User {
  // 主键
  private Integer id;
  // 用户名
  private String username;
  // 密码
  private String password;
  // 姓名
  private String name;
  public void setId(Integer id) {
    this.id = id;
  }
  @Override
  public String toString() {
    return "User{" +
        "id=" + id +
        ", username='" + username + '\'' +
        ", password='" + password + '\'' +
        ", name='" + name + '\'' +
        '}';
  }
  public Integer getId() {
    return id;
  }
  public String getUsername() {
    return username;
  }
  public void setUsername(String username) {
    this.username = username;
  }
  public String getPassword() {
    return password;
  }
  public void setPassword(String password) {
    this.password = password;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
}
UserService
package com.itheima.service;
import com.itheima.domain.User;
public interface UserService {
  public User queryUser(Integer id);
}

UserServiceImpl,下面的蓝色代码就是模仿redis,此时要注意下面的模拟redis的map集合必须放到下面的queryUser的外面,也就是说下面的userMap变量必须是成员变量,不然的话,因为redis是被多个线程共享的,如果你放到下面的queryUser()方法里面,那么就是多个线程有多个userMap集合,下面的代码就是如果查询到数据,那么就用redis里面的,如果查询不到就用数据库里面的

package com.itheima.service;
import com.itheima.domain.User;
import com.itheima.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class UserServiceImpl implements UserService {
  @Autowired
  private UserMapper userMapper;
  // 蓝色代码注释开始
  static Map<Integer,User> userMap=new HashMap();
  static {
    User huancun_user =new User();
    huancun_user.setId(1);
    huancun_user.setName("zhangsan");
    huancun_user.setPassword("123");
    huancun_user.setName("张三");
    userMap.put(1,huancun_user);
  }
  // 蓝色代码注释结束
  public User queryUser(Integer id){
    User user=userMap.get(id);
    if(user==null){
      user= userMapper.queryUser(id);
      System.out.println("查询数据库");
      userMap.put(user.getId(),user);
    }else{
      System.out.println("查询缓存");
    }
    return user;
  };
}
SpringbootApplication代码
package com.itheima;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringbootApplication {
  public static void main(String[] args) {
    SpringApplication.run(SpringbootApplication.class, args);
  }
}

数据库里面的数据如下

-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `username` varchar(50) DEFAULT NULL,
 `password` varchar(50) DEFAULT NULL,
 `name` varchar(50) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'zhangsan', '123', '张三');
INSERT INTO `user` VALUES ('2', 'lisi', '123', '李四');

然后我们查询下面的链接,因为此时上面的模拟redis的map集合里面没有id值是2的数据,所以此时都是查询数据库,你想这一下子10000并发过去,数据库会有很大压力的,

在这里插入图片描述

然后打印结果如下,就是打印了很多查询数据库和查询缓存,此时也就说明10000个并发里面有很多去查询了数据库,这个是要避免的,至于为什么有查询缓存的打印,因为我们把查询的数据给放到模拟的redis里面了啊,所以刚开始的一大部分线程都是查询数据库,然后剩下的都是查询模拟的redis缓存里面的数据

查询数据库
查询数据库
查询数据库
查询数据库
查询数据库
查询数据库
查询数据库
查询数据库
查询数据库
查询数据库
查询数据库
查询数据库
查询数据库
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存

然后我们使用双重检测锁来解决上面的缓存穿透

我们怎么解决缓存穿透呢,即使10000个并发过来,然后这10000个并发需要的数据在redis里面都没有,那么我们应该第一个线程查询数据里面的数据,然后把这个数据给放到redis里面,然后剩下的9999个线程都到redis里面查询,这样就解决了缓存穿透,所以我们可以把上面的代码变成下面这样

比如下面的代码

Pom.xml代码

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>springboot</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>
  <name>springboot</name>
  <description>Demo project for Spring Boot</description>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.1.RELEASE</version>
    <relativePath></relativePath> <!-- lookup parent from repository -->
  </parent>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>1.1.1</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>
Application.properties
server.port=8081
#DB Configuration:
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://47.91.248.236:3306/hello?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
#spring集成Mybatis环境
#pojo别名扫描包
mybatis.type-aliases-package=com.itheima.domain
#加载Mybatis映射文件
mybatis.mapper-locations=classpath:mapper/*Mapper.xml

MyController代码,下面的蓝色代码是模仿10000个并发线程

/**
 * sinture.com Inc.
 * Copyright (c) 2016-2018 All Rights Reserved.
 */
package com.itheima.controller;
import com.itheima.mapper.UserMapper;
import com.itheima.domain.User;
import com.itheima.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * @author xinzhu
 * @version Id: MyController.java, v 0.1 2018年12月05日 下午6:29 xinzhu Exp $
 */
@RestController
public class MyController {
  @Autowired
  private UserService userService;
  @RequestMapping("/hello/{id}")
  @ResponseBody
  public User queryUser(@PathVariable Integer id){
    // 蓝色代码注释开始
    new Thread(){
      @Override
      public void run() {
        for (int x = 0; x < 10000; x++) {
          userService.queryUser(id);
        }
      }
    }.start();
    // 蓝色代码注释结束
    return userService.queryUser(id);
  }
}
User类
/**
 * sinture.com Inc.
 * Copyright (c) 2016-2018 All Rights Reserved.
 */
package com.itheima.domain;
/**
 * @author xinzhu
 * @version Id: User.java, v 0.1 2018年12月06日 下午1:40 xinzhu Exp $
 */
public class User {
  // 主键
  private Integer id;
  // 用户名
  private String username;
  // 密码
  private String password;
  // 姓名
  private String name;
  public void setId(Integer id) {
    this.id = id;
  }
  @Override
  public String toString() {
    return "User{" +
        "id=" + id +
        ", username='" + username + '\'' +
        ", password='" + password + '\'' +
        ", name='" + name + '\'' +
        '}';
  }
  public Integer getId() {
    return id;
  }
  public String getUsername() {
    return username;
  }
  public void setUsername(String username) {
    this.username = username;
  }
  public String getPassword() {
    return password;
  }
  public void setPassword(String password) {
    this.password = password;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
}
UserService
package com.itheima.service;
import com.itheima.domain.User;
public interface UserService {
  public User queryUser(Integer id);
}

UserServiceImpl,下面的蓝色代码就是模仿redis,此时要注意下面的模拟redis的map集合必须放到下面的queryUser的外面,也就是说下面的userMap变量必须是成员变量,不然的话,因为redis是被多个线程共享的,如果你放到下面的queryUser()方法里面,那么就是多个线程有多个userMap集合,下面的代码就是如果查询到数据,那么就用redis里面的,如果查询不到就用数据库里面的

然后下面的红色代码就是解决上面的缓存穿透问题,使用锁来解决缓存穿透问题,而且叫做双重检测锁,为什么叫做双重检测锁呢,因为有两个if语句,第一个if语句就是为了减少走红色代码里面的同步代码块,因为如果换成里面存在想要的数据,那么就不需要走下面的红色代码里面的同步代码块了,所以有两个if语句,至于为什么要有下面的 user= userMap.get(id);,是因为第一次线程查询把数据放到模仿的redis缓存里面之后,剩下的线程当走到下面的同步代码块的时候,需要在查询一下缓存里面的数据就会发现刚刚第一个线程放到redis里面的数据了,所以才会有下面的红色代码里面的 user= userMap.get(id);

package com.itheima.service;
import com.itheima.domain.User;
import com.itheima.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class UserServiceImpl implements UserService {
  @Autowired
  private UserMapper userMapper;
  // 蓝色代码注释开始
  static Map<Integer,User> userMap=new HashMap();
  static {
    User huancun_user =new User();
    huancun_user.setId(1);
    huancun_user.setName("zhangsan");
    huancun_user.setPassword("123");
    huancun_user.setName("张三");
    userMap.put(1,huancun_user);
  }
  // 蓝色代码注释结束
  public User queryUser(Integer id){
    User user=userMap.get(id);
    // 红色代码注释开始
    if(user==null){
      synchronized (this) {
        user= userMap.get(id);
        if (null == user) {
          user= userMapper.queryUser(id);
          System.out.println("查询数据库");
          userMap.put(user.getId(),user);
        }else{
          System.out.println("查询缓存");
        }
      }
    }else{
      System.out.println("查询缓存");
    }
    //红色代码注释结束
    return user;
  };
}

数据库里面的数据如下

-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `username` varchar(50) DEFAULT NULL,
 `password` varchar(50) DEFAULT NULL,
 `name` varchar(50) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'zhangsan', '123', '张三');
INSERT INTO `user` VALUES ('2', 'lisi', '123', '李四');

然后我们查询下面的链接,因为此时上面的模拟redis的map集合里面没有id值是2的数据,所以此时都是查询数据库,你想这一下子10000并发过去,数据库会有很大压力的,

然后打印结果如下,就是就只有第一个打印了查询数据库,然后剩下的都是查询缓存了,这就是解决缓存穿透

查询数据库
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存
查询缓存

(0)

相关推荐

  • 详细介绍高性能Java缓存库Caffeine

    1.介绍 在本文中,我们来看看Caffeine- 一个高性能的 Java 缓存库. 缓存和 Map 之间的一个根本区别在于缓存可以回收存储的 item. 回收策略为在指定时间删除哪些对象.此策略直接影响缓存的命中率 - 缓存库的一个重要特征. Caffeine 因使用 Window TinyLfu 回收策略,提供了一个近乎最佳的命中率. 2.依赖 我们需要在 pom.xml 中添加 caffeine 依赖: <dependency> <groupId>com.github.ben-

  • Java Ehcache缓存框架入门级使用实例

    前言 JAVA缓存实现方案有很多,最基本的自己使用Map去构建缓存,或者使用memcached或Redis,但是上述两种缓存框架都要搭建服务器,而Map自行构建的缓存可能没有很高的使用效率,那么我们可以尝试一下使用Ehcache缓存框架. Ehcache主要基于内存缓存,磁盘缓存为辅的,使用起来方便.下面介绍如何在项目中使用Ehcache 入门使用教程 1.maven引用 <dependency> <groupId>net.sf.ehcache</groupId> &l

  • javaWeb中使用Redis缓存实例解析

    直接进入主题: 一:serviceImpl定义: @Service public class JedisClientSingleService implements JedisClient { @Autowired private JedisPool jedisPool; @Override public String get(String key) { Jedis jedis = jedisPool.getResource(); String string = jedis.get(key);

  • Java LocalCache 本地缓存的实现实例

    源码地址: GitHub 使用场景 在Java应用中,对于访问频率高,更新少的数据,通常的方案是将这类数据加入缓存中.相对从数据库中读取来说,读缓存效率会有很大提升. 在集群环境下,常用的分布式缓存有Redis.Memcached等.但在某些业务场景上,可能不需要去搭建一套复杂的分布式缓存系统,在单机环境下,通常是会希望使用内部的缓存(LocalCache). 实现 这里提供了两种LocalCache的实现,一种是基于ConcurrentHashMap实现基本本地缓存,另外一种是基于Linked

  • Java中LocalCache本地缓存实现代码

    前言 本次分享探讨java平台的本地缓存,是指占用JVM的heap区域来缓冲存储数据的缓存组件. 一.本地缓存应用场景 localcache有着极大的性能优势: 1. 单机情况下适当使用localcache会使应用的性能得到很大的提升. 2. 集群环境下对于敏感性要求不高的数据可以使用localcache,只配置简单的失效机制来保证数据的相对一致性. 哪些数据可以存储到本地缓存? 1.访问频繁的数据: 2.静态基础数据(长时间内不变的数据): 3.相对静态数据(短时间内不变的数据). 二.jav

  • JavaWeb禁止浏览器缓存当前Web页面的方法

    所谓浏览器缓存,是指当第一次访问网页时,浏览器会将这些网页缓存到本地,当下一次再访问这些被缓存的网页时,浏览器就会直接从本地读取这些网页的内容,而无需再从网络上获取. 虽然浏览器提供的缓存功能可以有效地提高网页的装载速度,但对于某些需要实时更新的网页,这种缓存机制就会影响网页的正常显示.幸好在HTTP响应消息头中提供了三个字段可以关闭客户端浏览器的缓存功能.下面三条语句分别使用这三个字段来关闭浏览器的缓存: response.setDateHeader("Expires", 0); r

  • Java模拟并解决缓存穿透问题

    什么叫做缓存穿透 缓存穿透只会发生在高并发的时候,就是当有10000个并发进行查询数据的时候,我们一般都会先去redis里面查询进行数据,但是如果redis里面没有这个数据的时候,那么这10000个并发里面就会有很大一部分并发会一下子都去mysql数据库里面进行查询了 解决缓存穿透 首先我模拟一下缓存穿透 比如下面的代码 Pom.xml代码 <?xml version="1.0" encoding="UTF-8"?> <project xmlns=

  • Redis使用元素删除的布隆过滤器来解决缓存穿透问题

    目录 前言 缓存雪崩 解决方案 缓存击穿 解决方案 缓存穿透 解决方案 布隆过滤器(Bloom Filter) 什么是布隆过滤器 位图(Bitmap) 哈希碰撞 布隆过滤器的2大特点 fpp 布隆过滤器的实现(Guava) 布隆过滤器的如何删除 带有计数器的布隆过滤器 总结 前言 在我们日常开发中,Redis使用场景最多的就是作为缓存和分布式锁等功能来使用,而其用作缓存最大的目的就是为了降低数据库访问.但是假如我们某些数据并不存在于Redis当中,那么请求还是会直接到达数据库,而一旦在同一时间大

  • java模拟hibernate一级缓存示例分享

    纯Java代码模拟Hibernate一级缓存原理,简单易懂. 复制代码 代码如下: import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map; public class LevelOneCache { //这个对象就是用来模拟hibernate一级缓存的 private static Map<Integer, Student> stus=new HashMap&l

  • redis缓存穿透解决方法

    缓存技术可以用来减轻数据库的压力,提升访问效率.目前在企业项目中对缓存也是越来越重视.但是缓存不是说随随便便加入项目就可以了.将缓存整合到项目中,这才是第一步.而缓存带来的穿透问题,进而导致的雪蹦问题都是我们迫切需要解决的问题.本篇文章将我平时项目中的解决方案分享给大家,以供参考. 一.缓存穿透的原理 缓存的正常使用如图: 如图所示,缓存的使用流程: 1.先从缓存中取数据,如果能取到,则直接返回数据给用户.这样不用访问数据库,减轻数据库的压力. 2.如果缓存中没有数据,就会访问数据库. 这里面就

  • Redis缓存穿透/击穿工具类的封装

    目录 1. 简单的步骤说明 2. 逻辑缓存数据类型 3. 缓冲工具类的封装 3.1 CacheClient 类的类图结构 3.2 CacheClient 类代码 1. 简单的步骤说明 创建一个逻辑缓存数据类型 封装缓冲穿透和缓冲击穿工具类 2. 逻辑缓存数据类型 这里主要是创建一个可以往Redis里边存放的数据类型 RedisData 的Java类型 import lombok.Data; import java.time.LocalDateTime; @Data public class Re

  • 详解缓存穿透击穿雪崩解决方案

    一:前言 设计一个缓存系统,不得不要考虑的问题就是:缓存穿透.缓存击穿与失效时的雪崩效应. 二:缓存穿透 缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义.在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞. 三:解决方案 有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足

  • 详解缓存穿透/击穿/雪崩原理及其解决方案

    目录 1. 简介 2. 缓存穿透 2.1描述 2.2 解决方案 3. 缓存击穿 3.1 描述 3.2 解决方案 4. 缓存雪崩 4.1 描述 4.1 解决方案 5. 布隆过滤器 5.1 描述 5.2 数据结构 5.3 "一定不在集合中" 5.4 "可能在集合中" 5.5 "删除困难" 5.6 为什么不使用HashMap呢? 1. 简介 如图所示,一个正常的请求 1.客户端请求张铁牛的博客. 2.服务首先会请求redis,查看请求的内容是否存在.

  • 详解Redis缓存穿透/击穿/雪崩原理及其解决方案

    目录 1.简介 2.缓存穿透 2.1描述 2.2解决方案 3.缓存击穿 3.1描述 3.2解决方案 4.缓存雪崩 4.1描述 4.1解决方案 5.布隆过滤器 5.1描述 5.2数据结构 5.3"一定不在集合中" 5.4"可能在集合中" 5.5"删除困难" 5.6为什么不使用HashMap呢? 1. 简介 如图所示,一个正常的请求 1.客户端请求张铁牛的博客. 2.服务首先会请求redis,查看请求的内容是否存在. 3.redis将请求结果返回给服

  • 详解Java redis中缓存穿透 缓存击穿 雪崩三种现象以及解决方法

    目录 前言 一.缓存穿透 二.缓存击穿 三.雪崩现象 总结 前言 本文主要阐述redis中的三种现象 1.缓存穿透 2.缓存击穿 3.雪崩现象 本文主要说明本人对三种情况的理解,如果需要知道redis基础请查看其他博客,加油! 一.缓存穿透 理解:何为缓存穿透,先要了解穿透,这样有助于区分穿透和击穿,穿透就类似于伤害一点一点的累计,最终打到穿透的目的,类似于射手,一下一下普通攻击,最终杀死对方,先上图 先来描述一下缓存穿透的过程: 1.由于我们取数据的原则是先查询redis上,如果redis上有

  • JAVA面试题之缓存击穿、缓存穿透、缓存雪崩的三者区别

    目录 调用链路 缓存击穿 含义: 解决方案: 缓存穿透 含义: 解决方案: 缓存雪崩 含义: 解决方案: 前端发起一个请求,经历过三次握手后连接到服务器,想要获取相应的数据,那么服务器接入了缓存中间件后,从接收到Request到最后的Response,到底是怎样的一个流程呢?以下探讨忽略掉参数校验等逻辑,直接讲最核心的链路. 调用链路 一个请求Request过来,服务器首先和缓存中间件建立连接,传输对应key到缓存中间件中获取相对应的数据,服务器拿到返回的结果后,判断返回的结果是否有数据,如果有

随机推荐