Spring Boot整合Redis与Cache实现缓存

Spring Boot 整合NoSQL

NoSQL 是指非关系型数据库,非关系型数据库和关系型数据库两者存在许多显著的不同点,其中最重要的是NoSQL 不使用SQL 作为查询语言。其数据存储可以不需要固定的表格模式,一般都有水平可扩展性的特征。NoSQL 主要有如下几种不同的分类:

  • Key/Value 键值存储。这种数据存储通常都是无数据结构的,一般被当作字符串或者二进制数据,但是数据加载速度快,典型的使用场景是处理高并发或者用于日志系统等,这一类的数据库有Redis 、Tokyo Cabinet 等。
  • 列式存储数据库。列存储数据库功能相对局限,但是查找速度快,容易进行分布式扩展,一般用于分布式文件系统中,这一类的数据库有HBase 、Cassandra 等。
  • 文档型数据库。和Key/Value 键值存储类似,文档型数据库也没有严格的数据格式,这既是缺点也是优势,因为不需要预先创建表结构,数据格式更加灵活, 一般可用在Web 应用中,这一类数据库有MongoDB 、CouchDB 等。
  • 图形数据库。图形数据库专注于构建关系图谱,例如社交网络,推荐系统等,这一类的数据库有Neo4J 、DEX 等。
    NoSQL种类繁多,Spring Boot对大多数NoSQL都提供了配置支持。

整合Redis

Redis 简介

Redis是一个使用C编写的基于内存的NoSQL 数据库,它是目前最流行的键值对存储数据库。Redis 由一个Key、Value映射的字典构成, 与其他NoSQL 不同, Redis 中Value 的类型不局限于字符串,还支持列表、集合、有序集合、散列等。Redis 不仅可以当作缓存使用,也可以配置数据持久化后当作NoSQL 数据库使用,目前支持两种持久化方式:快照持久化和AOF 持久化。另一方面,Redis也可以搭建集群或者主从复制结构,在高并发环境下具有高可用性。

Redis 安装

Redis安装可以参考:https://www.runoob.com/redis/redis-install.html

整合Spring Boot

Redis的Java客户端有很多,例如Jedis、JRedis、Spring Data Redis等,Spring Boot借助于Spring Data Redis为Redis提供了开箱即用自动化配置,开发者只需要添加相关依赖并配置Redis连接信息即可,具体整合步骤如下。

  1. 创建Spring Boot项目
    首先创建Spring Boot Web 项目,添加如下依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</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-web</artifactId>
</dependency>
  1. 配置Redis
    接下来在application.properties 中配置Redis 连接信息,代码如下:
# Redis属性配置
spring.redis.database=0
spring.redis.host=192.168.86.3
spring.redis.port=6379
spring.redis.password=password
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.max-wait=-1ms
spring.redis.jedis.pool.min-idle=0
# 缓存配置
spring.cache.cache-names=c1,c2
spring.cache.redis.time-to-live=1800s
  • 第1~4行是基本连接信息配置,第5~8 行是连接池信息配置。
  • 第1行配置表示使用的Redis库的编号,Redis中提供了16个database,编号为0~15 。
  • 第2行配置表示Redis 实例的地址。
  • 第3行配置表示Redis 立起口号, 默认是6379 。
  • 第4行配置表示Redis 登录密码。
  • 第5行配置表示Redis 连接池的最大连接数。
  • 第6行配置表示Redis 连接池中的最大空闲连接数。
  • 第7行配置表示连接池的最大阻塞等待时间,默认为-1 ,表示没有限制。第8行配置表示连接池最小空闲连接数。
  • 如果项目使用了Lettuce,则只需将第5-8行配置中的jedis修改为lettuce即可。

Spring Boot缓存

Spring 3.1 中开始对缓存提供支持,核心思路是对方法的缓存,当开发者调用一个方法时,将方法的参数和返回值作为key/value缓存起来,当再次调用该方法时,如果缓存中有数据,就直接从缓存中获取,否则再去执行该方法。但是, Spring 中并未提供缓存的实现,而是提供了一套缓存API,开发者可以自由选择缓存的实现,目前Spring Boot 支持的缓存有如下几种:

  • JCache (JSR-107)
  • EhCache 2.x
  • Hazelcast
  • lnfinispan
  • Couchbase
  • Redis
  • Caffeine
  • Simple
    本章将介绍目前常用的缓存实现Redis 。由于Spring 早己将缓存领域统一,因此无论使用哪种缓存实现,不同的只是缓存配置,开发者使用的缓存注解是一致的( Spring 缓存注解和各种缓存实现的关系就像JDBC 和各种数据库驱动的关系一样)。
    对于缓存
  • 内存的速度远远大于硬盘的速度
  • 缓存主要是在获取资源方便性能优化的关键方面
  • Redis 是缓存数据库
  • 缓存未命中解决与防止缓存击穿
  1. 开启缓存
    在项目的入口类上添加@EnableCaching 注解开启缓存,代码如下:
package org.gary.rediscache;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching
public class RediscacheApplication {

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

}

  1. 创建BookDao
    创建Book 实体类和BookService ,代码如下:
package org.gary.rediscache;

import java.io.Serializable;

public class Book implements Serializable {
    private Integer id;
    private String name;
    private String author;

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", author='" + author + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }
}

package org.gary.rediscache;

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;

@Service
@CacheConfig(cacheNames = "book_cache")
public class BookDao {
    @Cacheable
    public Book getBookById(Integer id) {
        System.out.println("getBookById");
        Book book = new Book();
        book.setId(id);
        book.setName("三国演义");
        book.setAuthor("罗贯中");
        return book;
    }

    @CachePut(key = "#book.id")
    public Book updateBookById(Book book) {
        System.out.println("updateBookById");
        book.setName("三国演义2");
        return book;
    }

    @CacheEvict(key = "#id")
    public void deleteBookById(Integer id) {
        System.out.println("deleteBookById");
    }
}

代码解释:
• 在BookDao上添加@CacheConfig 注解指明使用的缓存的名字,这个配置可选,若不使用@CacheConfig 注解,则直接在@Cacheable 注解中指明缓存名字。
• 第4 行在getBookByld 方法上添加@Cacheable 注解表示对该方法进行缓存,默认情况下,缓存的key是方法的参数,缓存的value是方法的返回值。当开发者在其他类中调用该方法时,首先会根据调用参数查看缓存中是否有相关数据,若有,则直才妾使用缓存数据,该方法不会执行,否则执行该方法,执行成功后将返回值缓存起来,但若是在当前类中调用该方法,则缓存不会生效。
• @Cacheable 注解中还有一个属性condition 用来描述缓存的执行时机,例如@Cacheable(condition =”#id%2==0”)表示当id 对2 取模为0 时才进行缓存,否则不缓存.
• 如果开发者不想使用默认的key,也可以像第13行和第19行一样自定义key,第13 行表示缓存的key 为参数book 对象中id 的值,第19 行表示缓存的key 为参数id . 除了这种使用参数定义key 的方式之外, Spring 还提供了一个root 对象用来生成key
• 如果这些key 不能够满足开发需求,开发者也可以自定义缓存key的生成器KeyGenerator,代码如下:

import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

@Component
public class MyKeyGenerator implements KeyGenerator {
    @Override
    public Object generate(Object target, Method method, Object... params) {
        return Arrays.toString(params);
    }
}

@Service
@CacheConfig(cacheNames = "book_cache")
public class BookDao {
    @Autowired
    MyKeyGenerator myKeyGenerator;
    @Cacheable(keyGenerator = "myKeyGenerator")
    public Book getBookById(Integer id) {
        System.out.println("getBookById");
        Book book = new Book();
        book.setId(id);
        book.setName("三国演义");
        book.setAuthor("罗贯中");
        return book;
    }
    @CachePut(key = "#book.id")
    public Book updateBookById(Book book) {
        System.out.println("updateBookById");
        book.setName("三国演义2");
        return book;
    }
    @CacheEvict(key = "#id")
    public void deleteBookById(Integer id) {
        System.out.println("deleteBookById");
    }
}

自定义MyKeyGenerator 实现KeyGenerator 接口,然后实现该接口中的generate 方法,该方法的三个参数分别是当前对象、当前请求的方法以及方法的参数,开发者可根据这些信息组成一个新的key 返回,返回值就是缓存的key。在@Cacheable 注解中引用MyKeyGenerator 实例即可。
• @CachePut主解一般用于数据更新方法上,与@Cacheable 注解不同,添加了@CachePut 注解的方法每次在执行时都不去检查缓存中是否有数据,而是直接执行方法,然后将方法的执行结果缓存起来,如果该key 对应的数据已经被缓存起来了,就会覆盖之前的数据,这样可以避免再次加载数据时获取到脏数据。同时,@CachePut 具有和@Cacheable 类似的属性,这里不再赘述。
• @CacheEvict 注解一般用于删除方法上,表示移除一个key 对应的缓存。@CacheEvict注解有两个特殊的属性:allEntries 和beforelnvocation,其中allEntries 表示是否将所有的缓存款据都移除, 默认为false,beforelnvocation表示是否在方法执行之前移除缓存中的数据,默认为false,即在方法执行之后移除缓存中的数据。

  1. 创建测试类
    创建测试类, 对Service 中的方法进行测试,代码如下:
package org.gary.rediscache;

import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;


@RunWith(SpringRunner.class)
@SpringBootTest
class RediscacheApplicationTests {
    @Autowired
    BookDao bookDao;

    @Test
    public void contextLoads() {
        bookDao.getBookById(1);
        bookDao.getBookById(1);
        bookDao.deleteBookById(1);
        Book b3 = bookDao.getBookById(1);
        System.out.println("b3:" + b3);
        Book b = new Book();
        b.setName("三国演义");
        b.setAuthor("罗贯中");
        b.setId(1);
        bookDao.updateBookById(b);
        Book b4 = bookDao.getBookById(1);
        System.out.println("b4:" + b4);
    }
}

执行该方法,控制台打印日志如图所示:
在这里插入图片描述
一开始执行了两个查询,但是查询方法只打印了一次,因为第二次使用了缓存。接下来执行了删除方法,删除方法执行完之后再次执行查询, 查询方法又被执行了,因为在删除方法中缓存己经被删除了。再接下来执行更新方法,更新方法中不仅更新数据,也更新了缓存,所以在最后的查询方法中, 查询方法日志没打印,说明该方法没执行,而是使用了缓存中的数据,而缓存中的数据已经被更新了。
Spring Boot整合Redis与Cache实现缓存Demo:添加链接描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值