springboot整合redis做缓存

之前的项目中,用到过redis,主要是使用redis做缓存,redis在web开发中使用的场景很多,其中缓存是其中一个很重要的使用场景,之所以用作缓存,得益于redis的读写数据,尤其是在读取数据的时候是直接走内存的,这样在高并发访问数据的时候,和查询数据库相比,redis读取数据的高效性、快速性的优势可见一斑,据说新浪单是每天的所有内容的统计的总的访问数量可以达到上百亿次,这种场景下,如果没有redis做数据缓存,根本无法支撑这么大的数据访问量,redis使用的时候可以单独利用客户端引入jar包操作即可,实际项目中都是和框架进行整合的比较多,此处演示利用springboot整合redis做缓存的简单过程。

1、搭建redis服务器,为简化搭建过程,这里是在windows下使用redis3.2进行模拟,redis的下载可到github上,

这里写图片描述

下载压缩包,解压到本地的目录,解压完毕,直接在目录下启动服务端和客户端即可,

这里写图片描述

看到这个图说明启动成功,整个搭建过程比较简单,不做过对说明,

2、安装mysql,由于本次要模拟缓存的效果,需要查询数据库数据,访问mysql,
为简化linux下安装mysql的过程,本次将直接在docker上通过启动一个mysql的镜像安装mysql,比单纯安装mysql要省事不少,关于docker的使用,此处不做详细介绍,大家可参考相关资料自己搭建试试,拓展一下知识面,

我提前下载好了mysql的镜像,可以直接从dockerhub上拉取,或者自己上传,
这里写图片描述

首先启动docker服务,
service docker start,
启动成功后,通过命令安装mysql,
这里写图片描述

从安装到启动mysql,只需要3个命令,参考图中所示,参考图中的命令,然后进入mysql,为本次的测试工程创建一个测试的数据库,

这里写图片描述

创建完毕,就可以用可视化工具连接docker上的mysql了,注意的如果linux上如果之前开启了mysql的服务,一定要先关闭掉,以免端口占用启动不成功,同时要把3306的端口在防火墙中打开,这样可视化工具才能访问,

这里写图片描述

在redis这个库下新建一个user表,做测试使用,比较简单,不做过多讲述,

3、准备工作做完,下面开始搭建工程,编写相关代码,
1)工程结构如图:
这里写图片描述

工程结构可根据自己喜好自定义,首先看pom文件,

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
        <relativePath />
    </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>

        <!-- mybatis 与 spring boot 2.x的整合包 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <!--mysql JDBC驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.39</version>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

    </dependencies>

这里只添加主要的依赖,其他的有需要再另外加,由于需要访问数据库,为简化开发,这里整合了mybatis,

下面看application.properties的配置文件,这里面存放了整合mybatis的配置和redis的配置:


server.port=8081

#数据库连接
spring.datasource.url=jdbc:mysql://192.168.127.133:3306/redis?useUnicode=true
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root

#mybatis配置
mybatis.type-aliases-package=com.acong.entity
mybatis.mapper-locations=classpath:mybatis/*.xml

#mybatis的下划线转驼峰配置
#mybatis.configuration.map-underscore-to-camel-case=true


#打印sql时的语句 ========================
logging.level.com.acong.dao = debug
logging.file=d:/logs/bsbdj.log

#另外一种打印语句的方式
#mybatis.configuration.log-impl=org.appache.ibatis.logging.stdout.StdOutImpl

#将themilef的默认缓存禁用,热加载生效
spring.thymeleaf.cache=false

#启用分页插件[或者程序中进行配置]
#pagehelper.helper-dialect=mysql
#pagehelper.reasonable=true

## Redis 配置
## Redis数据库索引(默认为0)
spring.redis.database=0
## Redis服务器地址
spring.redis.host=127.0.0.1
## Redis服务器连接端口
spring.redis.port=6379
## Redis服务器连接密码(默认为空)
spring.redis.password=
## 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
## 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
## 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
## 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
## 连接超时时间(毫秒)
spring.redis.timeout=1200

关于redis更相信的配置可参考其他资料;
logback-spring.xml 是日志文件,日志文件的位置注意不要放错了,接下来看User,这个类和数据库中的user表对应,注意这里一定要序列化,因为redis保存对象的时候要求对象是序列化的;

public class User implements Serializable{

    private static final long serialVersionUID = 1L;
    private int uid;
    private String userName;
    private String passWord;
    private int salary;
    public int getUid() {
        return uid;
    }
    public void setUid(int uid) {
        this.uid = uid;
    }
    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 int getSalary() {
        return salary;
    }
    public void setSalary(int salary) {
        this.salary = salary;
    }
    public User(int uid, String userName, String passWord, int salary) {
        super();
        this.uid = uid;
        this.userName = userName;
        this.passWord = passWord;
        this.salary = salary;
    }
    public User() {
        super();
        // TODO Auto-generated constructor stub
    }

}

UserDao接口中写了几个一会儿测试用的接口,比较简单,相信大家一看就懂了,

@Mapper
public interface UserDao {

    List<User> queryAll();

    User findUserById(int id);

    int updateUser(@Param("user") User user);

    int deleteUserById(int id);

}

下面来写service中的方法,主要包括user表的查询,数据修改,数据删除,在这三种情况下,分别对redis做相关的操作,

@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private RedisTemplate redisTemplate;

    public List<User> queryAll() {
        return userDao.queryAll();
    }

    /**
     * 获取用户策略:先从缓存中获取用户,没有则取数据表中 数据,再将数据写入缓存
     */
    public User findUserById(int id) {
        String key = "user_" + id;

        ValueOperations<String, User> operations = redisTemplate.opsForValue();

        boolean hasKey = redisTemplate.hasKey(key);
        if (hasKey) {
            User user = operations.get(key);
            System.out.println("==========从缓存中获得数据=========");
            System.out.println(user.getUserName());
            System.out.println("==============================");
            return user;
        } else {
            User user = userDao.findUserById(id);
            System.out.println("==========从数据表中获得数据=========");
            System.out.println(user.getUserName());
            System.out.println("==============================");

            // 写入缓存
            operations.set(key, user, 5, TimeUnit.HOURS);
            return user;
        }

    }

    /**
     * 更新用户策略:先更新数据表,成功之后,删除原来的缓存,再更新缓存
     */
    public int updateUser(User user) {
        ValueOperations<String, User> operations = redisTemplate.opsForValue();
        int result = userDao.updateUser(user);
        if (result != 0) {
            String key = "user_" + user.getUid();
            boolean haskey = redisTemplate.hasKey(key);
            if (haskey) {
                redisTemplate.delete(key);
                System.out.println("删除缓存中的key=========>" + key);
            }
            // 再将更新后的数据加入缓存
            User userNew = userDao.findUserById(user.getUid());
            if (userNew != null) {
                operations.set(key, userNew, 3, TimeUnit.HOURS);
            }
        }
        return result;
    }

    /**
     * 删除用户策略:删除数据表中数据,然后删除缓存
     */
    public int deleteUserById(int id) {
        int result = userDao.deleteUserById(id);
        String key = "user_" + id;
        if (result != 0) {
            boolean hasKey = redisTemplate.hasKey(key);
            if (hasKey) {
                redisTemplate.delete(key);
                System.out.println("删除了缓存中的key:" + key);
            }
        }
        return result;
    }

}

查询部分的代码,

    /**
     * 获取用户策略:先从缓存中获取用户,没有则取数据表中 数据,再将数据写入缓存
     */
    public User findUserById(int id) {
        String key = "user_" + id;

        ValueOperations<String, User> operations = redisTemplate.opsForValue();

        boolean hasKey = redisTemplate.hasKey(key);
        if (hasKey) {
            long start = System.currentTimeMillis();
            User user = operations.get(key);
            System.out.println("==========从缓存中获得数据=========");
            System.out.println(user.getUserName());
            System.out.println("==============================");
            long end = System.currentTimeMillis();
            System.out.println("查询redis花费的时间是:" + (end - start)+"s");
            return user;
        } else {
            long start = System.currentTimeMillis();
            User user = userDao.findUserById(id);
            System.out.println("==========从数据表中获得数据=========");
            System.out.println(user.getUserName());
            System.out.println("==============================");

            // 写入缓存
            operations.set(key, user, 5, TimeUnit.HOURS);
            long end = System.currentTimeMillis();
            System.out.println("查询mysql花费的时间是:" + (end - start)+"s");
            return user;
        }

    }

查询的主要逻辑是初次查找,首先会根据key在redis中查找,如果有,则将数据直接返回,不走数据库,如果redis中没有,先查数据库,然后将查询结果放入redis方便,下次再查询相同的数据时候,就不用访问数据库了,这也是很多网站的普遍做法,将读多写少,不经常发生变动的数据放入redis这样可以在用户数访问访问密集的时间点减少数据回源,以免对后端和数据库造成过大的压力,

看测试类,

@ResponseBody
    @RequestMapping("/findUserById")
    public Map<String, Object> findUserById(@RequestParam int id){
        User user = userService.findUserById(id);
        Map<String, Object> result = new HashMap<>();
        result.put("uid", user.getUid());
        result.put("uname", user.getUserName());
        result.put("pass", user.getPassWord());
        result.put("salary", user.getSalary());
        return result;
    }

查询数据库的user.xml对应的代码:

<select id="findUserById" resultType="com.acong.entity.User" parameterType="int">
        select * from user where uid = #{id}
    </select>

启动APP,浏览器输入:http://localhost:8081/test/findUserById?id=6
第一次查询的时候,走的是数据库,下面看查询结果和控制台的输出内容,

这里写图片描述

这里写图片描述

下面再访问一次看结果是否有变化,此时浏览器同样返回相同的数据,不同的是在控制台上,打印的内容不一样,
这里写图片描述

对比可以很明显看出来,第一次访问后将数据放入了redis,第二次再次访问相同的数据时,直接走了redis,查询的时间相差很大,说明redis的确在读取数据方面效率提高了很多,

至于数据的修改和删除,基本的思想是一致的,修改数据的时候,修改了数据库的数据,同时在redis中需要删除原来的数据对应的key值,然后再重新添加到redis中,删除数据的时候同样,删除了数据库的数据,还需要将redis中对应的数据的key进行删除,此处不再做详细讲述,贴上完整的代码,

controller代码,

@Controller
@RequestMapping("/test")
public class TestController {

    @Autowired
    private UserService userService;

    @ResponseBody
    @RequestMapping("/testController")
    public String testController(){

        return "success";
    }

    @ResponseBody
    @RequestMapping("/queryAll")
    public List<User> queryAll(){
        List<User> lists = userService.queryAll();
        return lists;
    }

    @ResponseBody
    @RequestMapping("/findUserById")
    public Map<String, Object> findUserById(@RequestParam int id){
        User user = userService.findUserById(id);
        Map<String, Object> result = new HashMap<>();
        result.put("uid", user.getUid());
        result.put("uname", user.getUserName());
        result.put("pass", user.getPassWord());
        result.put("salary", user.getSalary());
        return result;
    }

    @ResponseBody
    @RequestMapping("/updateUser")
    public String updateUser(){
        User user = new User();
        user.setUid(1);
        user.setUserName("cat");
        user.setPassWord("miaomiao");
        user.setSalary(4000);

        int result = userService.updateUser(user);

        if(result != 0){
            return "update user success";
        }

        return "fail";
    }

    @ResponseBody
    @RequestMapping("/deleteUserById")
    public String deleteUserById(@RequestParam int id){
        int result = userService.deleteUserById(id);
        if(result != 0){
            return "delete success";
        }
        return "delete fail";
    }
}

service层代码和接口dao的代码上面已经贴上了,

user.xml代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.acong.dao.UserDao">

    <select id="queryAll" resultType="com.acong.entity.User">
        select * from user
    </select>

    <select id="findUserById" resultType="com.acong.entity.User" parameterType="int">
        select * from user where uid = #{id}
    </select>

    <update id="updateUser" parameterType="com.acong.entity.User">
        update user 
        <trim prefix="set" suffixOverrides=",">
            <if test="user.userName != null and user.userName != '' ">
                username = #{user.userName},
            </if>
            <if test="user.passWord != null and user.passWord != ''">
                password = #{user.passWord},
            </if>
            <if test="user.salary != 0">
                salary = #{user.salary}
            </if>
        </trim>
        where uid = #{user.uid}
    </update>

    <delete id="deleteUserById" parameterType="int">
        delete from user where uid = #{id}
    </delete>

</mapper>

关于在项目中使用redis的代码部分,写法比较多,我这里采用了redisTemplate封装好的包,大家也可以使用jedis和jedispool对应的API操作redis也是可以的,实际中使用的比较多也比较正式的做法是先全局初始化redis的先关配置,然后再使用,同时对于操作redis的key,也会按照一定的配置策略做相关的工具类的封装与配置,便于运维过程中redis的管理方便。

本篇代码地址:https://download.csdn.net/download/zhangcongyi420/10664285

本篇有关springboot整合使用redis做缓存的使用到此结束,感谢来访。

猜你喜欢

转载自blog.csdn.net/zhangcongyi420/article/details/82686702