springboot集成redis做缓存,0基础入门,含源码

一.缓存概念

1.什么是缓存

日常生活中经常会听到缓存这个词,那到底什么是缓存呢?其实缓存就是数据交换的缓冲区(称作Cache),是临时存贮数据(使用频繁的数据)的地方。当用户查询数据,首先在缓存中寻找,如果找到了则直接执行;如果找不到则去数据库中查找。

缓存的本质就是用空间换时间,牺牲数据的实时性,以服务器内存中的数据暂时代替从数据库读取最新的数据,减少数据库IO,减轻服务器压力,减少网络延迟,加快页面打开速度。

2缓存的优缺点

优点:
加快了响应速度
减少了对数据库的读操作,数据库的压力降低
缺点:
内存容量相对硬盘小
缓存中的数据可能与数据库中数据不一致
内存断电就会清空数据,造成数据丢失

3.为什么使用缓存

一般在远端服务器上,考虑到客户端请求量多,某些数据请求量大,这些热点数据要频繁的从数据库中读取,给数据库造成压力,导致响应客户端较慢。所以,在一些不考虑实时性的数据中,经常将这些数据存在内存中,当请求时候,能够直接读取内存中的数据及时响应。

缓存主要有解决高性能与高并发与减少数据库压力。缓存本质就是将数据存储在内存中,当数据没有发生本质变化的时候,应尽量避免直接连接数据库进行查询,因为并发高时很可能会将数据库压塌,而是应去缓存中读取数据,只有缓存中未查找到时再去数据库中查询,这样就大大降低了数据库的读写次数,增加系统的性能和能提供的并发量。

二.redis概念

1.Redis简介

Redis 是一个高性能的 Key-Value 开源数据库, 是一个非关系型的数据库,是为了解决高并发、高扩展,大数据存储等一系列的问题而产生的数据库解决方案。但它不能替代关系型数据库,只能作为特定环境下的扩充。

2为什么用Redis作为缓存

  • 支持高可用: Redis 支持 master\slave 主\从机制、sentinal 哨兵模式、cluster 集群模式,大大保证了
    Redis 运行的稳定和高可用性。
  • 支持多种数据结构: Redis 不仅支持简单的 Key/Value 类型的数据,同时还提供 list、set、zset、hash
    等数据结构的存储。
  • 支持数据持久化: 可以将内存中的数据持久化在磁盘中,当宕机或者故障重启时,可以再次加载进如 Redis,从而不会或减少数据的丢失。
  • 生态完善: Redis 已成为业界内缓存的首选目标,所以很多语言和工具对其支持。

3. 支持的数据类型

Redis 支持的数据结构类型包括:

字符串(string)
哈希表(hash)
列表(list)
集合(set)
有序集合(zset)

为了保证读取效率,Redis 把数据对象存储在内存中,并支持周期性的把更新的数据写入磁盘文件中。而且它还提供了交集和并集,以及一些不同方式排序的操作。

redis的特性决定了它的功能,它可以用来做以下这些事情!

1.排行榜,利用zset可以方便的实现排序功能
2.计数器,利用redis中原子性的自增操作,可以统计到阅读量,点赞量等功能
3.简单消息队列,list存储结构,满足先进先出的原则,可以使用lpush/rpop或rpush/lpop实现简单消息队列
4.session共享,分布式系统中,可以利用redis实现session共享。spring官方提供的分布式解决方案Spring Session就是利用redis 实现的

4、缓存常见问题

1. 缓存穿透

2. 缓存击穿

3. 缓存雪崩

4. 缓存一致性

三.SpringBoot整合redis做缓存

1、使用redis缓存

  1. 引入redis依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 修改项目启动类
    增加注解@EnableCaching,开启缓存功能,如下:
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;

@Slf4j
@SpringBootApplication
@MapperScan("com.gsj.boot.mapper")
@EnableCaching
public class BootApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(BootApplication.class, args);
        Environment bean = context.getBean(Environment.class);
        log.info("启动成功");
        log.info("连接数据库成功"+bean.getProperty("spring.datasource.url"));
    }

}
  1. 配置redis数据库
    在application.yaml中配置Redis连接信息,如下:
server:
  port: 8090
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.gsj.boot.model
  config-location: classpath:/mybatis-config.xml

#配置数据源
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
  servlet:
    multipart:
      enabled: true
      max-file-size: 10MB
#  springboot高版本与swagger2版本不兼容,添加以下配置可解决
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher
  redis:
    # redis库
    database: 0
    # redis 服务器地址
    host: localhost
    # redis 端口号
    port: 6379
    # redis 密码
    password:
    # 连接超时时间(毫秒)
    timeout: 100000
    lettuce:
      pool:
        # 连接池最大链接数(负数表示没有限制)
        max-active: 8
        # 连接池最大阻塞等待时间(负数表示没有限制)
        max-wait: -1
        # 连接池最大空闲连接数
        max-idle: 8
        # 连接池最小空闲连接数
        min-idle: 0






  1. 创建redis配置类
    新建Redis缓存配置类,如下:
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.*;

import java.time.Duration;

/**
 * Redis 配置类
 */
@Configuration
public class RedisConfig {

    /**
     * 配置缓存管理器
     * @param factory Redis 线程安全连接工厂
     * @return 缓存管理器
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        // 生成两套默认配置,通过 Config 对象即可对缓存进行自定义配置
        RedisCacheConfiguration cacheConfig1 = RedisCacheConfiguration.defaultCacheConfig()
                // 设置过期时间 10 分钟
                .entryTtl(Duration.ofMinutes(10))
                // 设置缓存前缀
                .prefixKeysWith("cache:user:")
                // 禁止缓存 null 值
                .disableCachingNullValues()
                // 设置 key 序列化
                .serializeKeysWith(keyPair())
                // 设置 value 序列化
                .serializeValuesWith(valuePair());

        RedisCacheConfiguration cacheConfig2 = RedisCacheConfiguration.defaultCacheConfig()
                // 设置过期时间 30 秒
                .entryTtl(Duration.ofSeconds(30))
                .prefixKeysWith("cache:admin:")
                .disableCachingNullValues()
                .serializeKeysWith(keyPair())
                .serializeValuesWith(valuePair());

        // 返回 Redis 缓存管理器
        return RedisCacheManager.builder(factory)
                .withCacheConfiguration("user", cacheConfig1)
                .withCacheConfiguration("admin", cacheConfig2)
                .build();
    }

    /**
     * 配置键序列化
     * @return StringRedisSerializer
     */
    private RedisSerializationContext.SerializationPair<String> keyPair() {
        return RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer());
    }

    /**
     * 配置值序列化,使用 GenericJackson2JsonRedisSerializer 替换默认序列化
     * @return GenericJackson2JsonRedisSerializer
     */
    private RedisSerializationContext.SerializationPair<Object> valuePair() {
        return RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer());
    }

}
  1. 操作redis

SpringBoot提供了两个bean来操作redis,分别是RedisTemplate 和 StringRedisTemplate,这两者的主要区别如下:

RedisTemplate使用的是JdkSerializationRedisSerializer ,存入数据会将数据先序列化成字节数组然后在存入Redis数据库;

StringRedisTemplate使用的是StringRedisSerializer。

示例如下:

@RestController
public class UserController {

    @Autowired
    private UserService userServer;
    
    @Autowired
    StringRedisTemplate stringRedisTemplate;

    
    /**
     * 查询所有课程
     */
    @RequestMapping("/allCourses")
    public String findAll() {
        List<Courses> courses = userServer.findAll();
        
        // 将查询结果写入redis缓存
        stringRedisTemplate.opsForValue().set("hot", String.valueOf(courses));
        
        // 读取redis缓存
        System.out.println(stringRedisTemplate.opsForValue().get("courses"));

        return "ok";
    }
}

2、使用 SpringCache 的注解

1. 注解说明
7. @CacheConfig: 一般配置在类上,指定缓存名称,这个名称是和上面“置缓存管理器”中缓存名称的一致。
8. @Cacheable: 用于对方法返回结果进行缓存,如果已经存在该缓存,则直接从缓存中获取,缓存的key可以从入参中指定,缓存的 value 为方法返回值。
9. @CachePut: 无论是否存在该缓存,每次都会重新添加缓存,缓存的key可以从入参中指定,缓存的value为方法返回值,常用作于更新。
10. @CacheEvict: 用于清除缓存
11. @Caching: 用于一次性设置多个缓存。

2. 常用注解配置参数

  1. value:
    缓存管理器中配置的缓存的名称,这里可以理解为一个组的概念,缓存管理器中可以有多套缓存配置,每套都有一个名称,类似于组名,这个可以配置这个值,选择使用哪个缓存的名称,配置后就会应用那个缓存名称对应的配置。

  2. key: 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合。

  3. condition: 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存。
    unless: 不缓存的条件,和 condition 一样,也是 SpEL 编写,返回 true 或者 false,为 true
    时则不进行缓存。

3. 自动缓存

  1. @Cacheable可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。

  2. 如果添加了@Cacheable注解,那么方法被调用后,值会被存入redis,下次再调用的时候会直接从redis中取值返回。

@Service
@CacheConfig(cacheNames = "user")
public class UserService {

    @Autowired
    private UserMapper userMapper;
    
    // 获取全部用户
    @Cacheable(key = "'allUsers'", unless = "#result==null")
    public List<Courses> findAll() {
        return userMapper.allUsers();
    }
}

四.案例说明

源码:

https://gitee.com/guo-shujiang/springboot-redis-cache

数据库:

DROP TABLE IF EXISTS `users`;
CREATE TABLE `users`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `userName` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户名',
  `passWord` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',
  `user_sex` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `nick_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 31 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES (1, '1', '1', '1', '1');
INSERT INTO `users` VALUES (28, 'aa', 'a123456', 'UserSexEnum.MAN', 'neo');
INSERT INTO `users` VALUES (29, 'bb', 'b123456', 'UserSexEnum.MAN', NULL);
INSERT INTO `users` VALUES (30, 'cc', 'b123456', 'UserSexEnum.MAN', NULL);

SET FOREIGN_KEY_CHECKS = 1;

1、创建实体类(model层)

import lombok.Data;

/**
 * @program: mybatisDriud
 * @description:
 * @author: 郭书江
 * @create: 2022-06-29 09:20
 **/
@Data
public class Users {
    private Integer age;
    private String name;
    private String sex;

    public Users() {
    }

    public Users(Integer age, String name, String sex) {
        this.age = age;
        this.name = name;
        this.sex = sex;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

2、创建接口(mapper层)

import com.gsj.boot.model.User;
import com.gsj.boot.model.UserTb;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

/**
 * @program: mybatislx
 * @description:
 * @author: 郭书江
 * @create: 2022-05-20 09:43
 **/
@Mapper
public interface UserMapper {

        List<User> getAll();

        User getOne(Long id);

        int insert(User user);

        int update(String userName,String passWord);

        int delete(Long id);

        List<UserTb> getBigData();

}

3.创建服务类(service层)

import com.gsj.boot.model.User;
import com.gsj.boot.model.UserTb;

import java.util.List;

/**
 * @program: demo
 * @description:
 * @author: 郭书江
 * @create: 2022-06-30 09:53
 **/
public interface UserService {
    List<User> getAll();

    User getOne(Long id);

    int insert(User user);

    int delete(Long id);

    int update(String userName,String passWord);
    List<UserTb> getBigData();
}

4、创建实现层(serviceimpl层)

/**
 * @program: mybatisDriud
 * @description:
 * @author: 郭书江
 * @create: 2022-06-29 09:19
 **/
import com.gsj.boot.mapper.UserMapper;
import com.gsj.boot.model.User;
import com.gsj.boot.model.UserTb;
import com.gsj.boot.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Slf4j
@Service
@CacheConfig(cacheNames = "user")
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    @Cacheable(value = "users",key = "'allUsers'", unless = "#result==null")
    public List<User> getAll() {
        List<User>  all= userMapper.getAll();
        return all;
    }

    @Override
    public User getOne(Long id) {
        return null;
    }

    @Override
    public int insert(User user) {
        int insert = userMapper.insert(user);
        return insert;
    }

    @Override
    @CacheEvict(key = "#id")
    public int delete(Long id) {
        int delete = userMapper.delete(id);
        return delete;
    }

    @Override
    @CachePut(key = "#user.userName")
    public int update(String userName, String passWord) {
        int update = userMapper.update(userName, passWord);
        return update;
    }

    @Override
    @Cacheable(value ="userTb" ,key = "'bigUserData'", unless = "#result==null")
    public List<UserTb> getBigData() {
        List<UserTb> bigData = userMapper.getBigData();
        return bigData;
    }


}

5、创建控制器(controller层)

import com.gsj.boot.mapper.UserMapper;
import com.gsj.boot.model.User;
import com.gsj.boot.model.UserTb;
import com.gsj.boot.model.Users;
import com.gsj.boot.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
@Slf4j
@RestController
public class UserController {
@Autowired
private UserMapper userMapper;
@Autowired
private UserService userService;

@GetMapping("/add")
public Integer add(){
    User users = new User("1","李四","男");
    int insert = userService.insert(users);
    return insert;
}
@GetMapping("/get")
public List<User> get(){
    long a = System.currentTimeMillis();
    List<User> all1 = userService.getAll();
    long b = System.currentTimeMillis();
    log.info("时间"+(b-a));
    return all1;
}
@GetMapping("/getBigData")
public Integer getBigData(){
    long a = System.currentTimeMillis();
    List<UserTb> all1 = userService.getBigData();
    long b = System.currentTimeMillis();
    log.info("时间"+(b-a));
    return all1.size();
}
@GetMapping("/getOne")
public User getOne(@RequestParam Long id){
    User all = userMapper.getOne(id);
    return all;
}
@GetMapping("/del")
public Integer del(@RequestParam Long id){
    int delete = userService.delete(id);
    return delete;
}
@GetMapping("/selectUsers")
public Users selectUsers(@RequestParam String key){
    return null;
}
@PostMapping
public Integer update(@RequestParam String userName,@RequestParam String passWord){
    int update = userService.update(userName, passWord);
    return update;
}

}

6、mapper.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.gsj.boot.mapper.UserMapper" >

    <!--<resultMap id="userMap" type="com.gsj.boot.model.User">
        <id column="id" property="id" jdbcType="BIGINT" ></id>
        <result column="userName" property="userName" jdbcType="VARCHAR" ></result>
        <result column="passWord" property="passWord" jdbcType="VARCHAR" ></result>
        <result column="user_sex" property="userSex" jdbcType="VARCHAR"></result>
        <result column="nick_name" property="nickName" jdbcType="VARCHAR" ></result>
    </resultMap>
    <sql id="Base_Column_List" >
        id, userName, passWord, user_sex, nick_name
    </sql>-->
     <select id="getAll" resultType="com.gsj.boot.model.User">
         select * from users
     </select>
    <select id="getBigData" resultType="com.gsj.boot.model.UserTb">
         select * from bigdata.usertb
     </select>
    <!--<select id="getAll" resultType="com.gsj.boot.model.User">
        ${sql}
    </select>-->
    <!--<select id="getMyUserAll" resultMap="userMap">
        select <include refid="Base_Column_List" /> from users
    </select>-->

    <!--<select id="getOne"  resultMap="userMap" >
        SELECT
        <include refid="Base_Column_List" />
        FROM users
        WHERE id = #{id}
    </select>-->






    <insert id="insert" parameterType="com.gsj.boot.model.User" >
       INSERT INTO
              users
              (userName,passWord,user_sex)
           VALUES
              (#{userName}, #{passWord}, #{userSex})
    </insert>
    <!--<insert id="insertsql" >
       ${sql}
    </insert>-->

    <update id="update" parameterType="String" >
        UPDATE
        users
        SET
        <if test="passWord != null">passWord = #{passWord}</if>
        WHERE
        userName = #{userName}
    </update>

    <delete id="delete" parameterType="java.lang.Long" >
       DELETE FROM
             users
       WHERE   id =#{id}
    </delete>
</mapper>

7、展示结果

第一次调用用时1879毫秒,第二次存入了redis中,用时4毫秒
在这里插入图片描述

在这里插入图片描述
那么我们来看一下redis
在这里插入图片描述
这就是存在redis中的数据

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值