SpringBoot教程
SpringBoot入门案例
SpringBoot框架Web开发
SpringBoot非web应用程序
SpringBoot使用拦截器
SpringBoot中使用Servlet
SpringBoot中使用Filter
SpringBoot项目配置字符编码
SpringBoot打包与部署
SpringBoot使用Actuator
SpringBoot集成Thymeleaf模板
SpringBoot总结及综合案例
SpringBoot工程下使用Mybatis反向工程

SpringBoot集成Redis

SpringBoot 集成Redis单机模式

项目名称:016-springboot-redis

1. 案例思路

完善根据学生id查询学生的功能,先从redis缓存中查找,如果找不到,再从数据库中查找,然后放到redis缓存中。

2. 实现步骤

首先通过MyBatis逆向工程生成实体bean和数据持久层。

① 在pom.xml文件中添加redis依赖

<!-- 加载spring boot redis包 -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

② 在Spring Boot核心配置文件application.properties中配置redis连接信息

完整application.properties配置文件如下:



#配置内嵌Tomcat端口号
server.port=9090

#配置项目上下文根
server.servlet.context-path=/016-springboot-redis

#配置连接MySQL数据库信息
spring.datasource.url=jdbc:mysql://192.168.92.134:3306/springboot?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456


#配置视图解析器
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp


#配置redis连接信息(单机模式)
spring.redis.host=192.168.92.134
spring.redis.port=6379
spring.redis.password=123456

③ 启动redis服务

④ RedisController类

RestController
public class RedisController {

    @Autowired
    private StudentService studentService;


    /**
     * 请求地址:http://localhost:9090/016-springboot-redis//springboot/allStudentCount
     * @param request
     * @return
     */
    @GetMapping(value = "/springboot/allStudentCount")
    public Object allStudentCount(HttpServletRequest request) {

        Long allStudentCount = studentService.queryAllStudentCount();

        return "学生总人数:" + allStudentCount;
    }
}

⑤ StudentService接口

public interface StudentService {

    /**
     * 获取学生总人数
     * @return
     */
    Long queryAllStudentCount();
}

⑥ 在StudentServiceImpl中注入RedisTemplate并修改根据id获取学生的方法配置了上面的步骤,Spring Boot将自动配置RedisTemplate,在需要操作redis的类中注入redisTemplate即可。

注意:Spring Boot帮我们注入RedisTemplate类,泛型里面只能写 、或者什么都不写。

@Service("studentServiceImpl")
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public Long queryAllStudentCount() {

        //设置redisTemplate对象key的序列化方式
        redisTemplate.setKeySerializer(new StringRedisSerializer());

        //从redis缓存中获取总人数
        Long allStudentCount = (Long) redisTemplate.opsForValue().get("allStudentCount");

        //判断是否为空
        if (null == allStudentCount) {

            //去数据库查询,并存放到redis缓存中
            allStudentCount = studentMapper.selectAllStudentCount();

            redisTemplate.opsForValue().set("allStudentCount",allStudentCount,15, TimeUnit.SECONDS);

        }


        return allStudentCount;
    }
}

⑦ 启动类Application在SpringBoot启动类上添加扫描数据持久层的注解并指定扫描包

@SpringBootApplication
@MapperScan(basePackages = "com.sxbdqn.springboot.mapper")//扫描数据持久层
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

⑧ 让Student类实现序列化接口(可选)在类名上Alt + 回车,如果没有提示生成序列化id,那么需要做如下的配置

⑨ 启动SpringBoot应用,访问测试

⑩ 打开Redis Desktop Mananger查看Redis中的情况

缓存穿透现象

项目名称:017-springboot-redis-synchronized

首先通过MyBatis逆向工程生成实体bean和数据持久层。

1.pom文件

<?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>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.sxbdqn.springboot</groupId>
    <artifactId>017-springboot-redis-synchronized</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <name>017-springboot-redis-synchronized</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!--SpringBoot web项目起步依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--MyBatis集成SpringBoot框架的起步依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>

        <!--连接MySQL的驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- 加载spring boot redis包 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>

        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>

        <plugins>

            <!--mybatis代码自动生成插件-->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.7</version>
                <configuration>
                    <!--配置文件的位置-->
                    <configurationFile>GeneratorMapper.xml</configurationFile>
                    <verbose>true</verbose>
                    <overwrite>true</overwrite>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2.核心配置文件application.properties

#配置内嵌Tomcat端口号
server.port=9090

#配置项目上下文根
server.servlet.context-path=/017-springboot-redis-synchronized

#配置MySQL数据库连接
spring.datasource.url=jdbc:mysql://192.168.92.134:3306/springboot?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456

#配置redis缓存
spring.redis.host=192.168.92.134
spring.redis.port=6379
spring.redis.password=123456

#配置SpringMVC视图解析器
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp

3.Dao数据持久层

4.Service业务层

@Service("studentServiceImpl")
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    @Override
    public Long queryAllStudentCount() {

        //设置redis的key序列化方式
        redisTemplate.setKeySerializer(new StringRedisSerializer());

        //从redis中获取学生总人数
        Long allStudentCount = (Long) redisTemplate.opsForValue().get("allStudentCount");

        //判断是否为空
        if (null == allStudentCount) {

            System.out.println("查询数据库。。。。。");

            //从数据库查询
            allStudentCount = studentMapper.selectAllStudentCount();

            //将值再存放到redis缓存中
            redisTemplate.opsForValue().set("allStudentCount", allStudentCount, 15, TimeUnit.HOURS);
        } else {

            System.out.println("查找Redis。。。。。");
        }

        return allStudentCount;
    }
}

5.Controller层

@RestController
public class RedisController {

    @Autowired
    private StudentService studentService;

    @GetMapping(value = "/springBoot/student")
    public @ResponseBody Object student() {

        //线程池个数,一般建议是CPU内核数 或者 CPU内核数据*2
        ExecutorService executorService = Executors.newFixedThreadPool(8);

        for (int i = 0; i < 1000; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                  studentService.queryAllStudentCount();
                }
            });
        }


        return "学生总人数:" + studentService.queryAllStudentCount();
    }
}

6.启动类

@SpringBootApplication
@MapperScan(basePackages = "com.sxbdqn.springboot.mapper")
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

7.启动应用程序,浏览器访问测试

8.造成的问题

Tip:多个线程都去查询数据库,这种现象就叫做缓存穿透,如果并发比较大,对数据库的压力过大,有可能造成数据库宕机。

缓存穿透现象-解决方法

项目名称:018-springboot-redis-synchronized

项目描述:018-springboot-redis-synchronized项目是在017-springboot-redis-synchronized项目基础上进行解决缓存穿透现象

1. 修改StudentServiceImpl中的代码


@Service("studentServiceImpl")
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    @Override
    public Long queryAllStudentCount() {
        //设置redisTemplate对象的key的序列化方式
        redisTemplate.setKeySerializer(new StringRedisSerializer());

        //从redis缓存中获取学生总人数
        Long allStudentCount = (Long) redisTemplate.opsForValue().get("allStudentCount");

        //判断学生总人数是否为空
        if (null == allStudentCount) {

            //设置同步代码块
            synchronized (this) {

                //再次从redis缓存中获取学生总人数
                allStudentCount = (Long) redisTemplate.opsForValue().get("allStudentCount");

                //双重检测判断缓存中是否有数据
                if (null == allStudentCount) {

                    System.out.println("从数据库获取数据");

                    //从数据库获取学生总人数
                    allStudentCount = studentMapper.selectAllStudentCount();

                    //将此值存放到redis缓存中
                    redisTemplate.opsForValue().set("allStudentCount", allStudentCount, 15, TimeUnit.MINUTES);
                } else {

                    System.out.println("从Redis中获取数据。。。。");

                }
            }

        } else {
            System.out.println("从Redis中获取数据。。。。");
        }

        return allStudentCount;
    }
}

2. 启动应用程序,浏览器访问测试,查看控制台输出只有第一个线程查询数据库,其它线程查询Redis缓存,这样的解决的小问题就是第一批进来的用户会有一个等待,但是这样的影响可以忽略。

3. springboot集成Redis阻止缓存穿透,为什么要做双层验证

防止线程获取到cpu执行权限的时候,其他线程已经将数据放到Redis中了,所以再次判断;

不能将synchronized范围扩大,因为如果Redis缓存中如果有数据,线程不应该同步,否则影响效率。

SpringBoot集成Redis哨兵模式

1. 在pom.xml中配置相关的jar依赖

这步我们前面已经做过


<!-- 加载spring boot redis包 -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2. 在Springboot核心配置文件application.properties中配置redis连接信息

IP地址及master根据自己的情况进行修改


#哨兵模式redis集群配置(哨兵模式)
spring.redis.password=123456
spring.redis.sentinel.master=mymaster
spring.redis.sentinel.nodes=192.168.235.128:26380,192.168.235.128:26382,192.168.235.128:26384

3. 通过rz –y命令,用我提供的资源中的配置文件覆盖Linux服务器上Redis的配置文件,注意根据自己机器的情况修改资源文件ip及引用的redis路径

我们目前6384是主,6380和6382是从

4. 启动三台Redis服务器

5. 在Xshell中开三个窗口,启动三台Redis哨兵

6. 在Redis Desktop Manager上创建三个连接,连接6380,6382,6384Redis服务器 

7. 清空6384主数据,查看情况

6380和6382也跟着清空。

8. 启动SpringBoot主程序,浏览器访问,查看Redis情况

Redis6380、6382、6384中都会存入数据。

9. 将Redis6384主停止再启动

哨兵会将Redis6382作为主,等Redis6384再次启动后,会作为Redis6382的从。