SpringBoot集成Redis作数据缓存

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: SpringBoot集成Redis作数据缓存


@[Toc]


一、缓存简介

Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 Redis),而是一个对缓存使用的抽象,通过在既有代码中添加少量它定义的各种 annotation,即能够达到缓存方法的返回对象的效果。

目前 Spring Boot 支持的缓存有如下几种:

  • JCache (JSR-107)
  • EhCache 2.x
  • Hazelcast
  • lnfinispan
  • Couchbase
  • Redis
  • Caffeine
  • Simple

目前常用的缓存实现 Ehcache 2.x和 Redis。由于 Spring 早己将缓存领域统一 ,因此无论使用哪种缓存实现,不同的只是缓存配置,开发者使用的缓存注解是一致的( Spring 缓存注解和各缓存实现的关系就像 JDBC和各种数据库驱动的关系)。

这里学习使用Redis作为缓存实现。


二、Redis缓存


1、添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>


2、属性配置

在application.properties中添加配置:

spring.redis.host=localhost
spring.redis.password=
# 一般来说是不用配置的,Spring Cache 会根据依赖的包自行装配
spring.cache.type=redis
# 连接超时时间(毫秒)
spring.redis.timeout=10000
# Redis默认情况下有16个分片,这里配置具体使用的分片
spring.redis.database=0
# 连接池最大连接数(使用负值表示没有限制) 默认 8
spring.redis.lettuce.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
spring.redis.lettuce.pool.max-wait=-1
# 连接池中的最大空闲连接 默认 8
spring.redis.lettuce.pool.max-idle=8
# 连接池中的最小空闲连接 默认 0
spring.redis.lettuce.pool.min-idle=0


3、 Cache核心注解


spring-boot-starter-cache 是 Spring Boot 体系内提供使⽤ Spring Cache的Starter 包。其中最核⼼的三个注解:@Cacheable、@CacheEvict、@CachePut。

3.1、@Cacheable

@Cacheable⽤来声明⽅法是可缓存的,将结果存储到缓存中以便后续使⽤相同参数调⽤时不需执⾏实际的⽅法,直接从缓存中取值。@Cacheable 可以标记在⼀个⽅法上,也可以标记在⼀个类上。当标记在⼀个⽅法上时表示该⽅法是⽀持缓存的,当标记在⼀个类上时则表示该类所有的⽅法都是⽀持缓存的。

例如:

@RequestMapping("/hello")
@Cacheable(value="helloCache")
public String hello(String name) {
 System.out.println("没有⾛缓存!");
 return "hello "+name;
}

@Cacheable ⽀持如下⼏个参数。

  • value:缓存的名称。
  • key:缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写;如果不指定,则缺省按照⽅法的所有参数进⾏组合。
  • condition:触发条件,只有满⾜条件的情况才会加⼊缓存,默认为空,既表示全部都加⼊缓存,⽀持SpEL。

把上⾯的⽅法稍微更改:

@RequestMapping("/condition")
@Cacheable(value="condition",condition="#name.length() <= 4")
public String condition(String name) {
 System.out.println("没有⾛缓存!");
 return "hello "+name;
}


3.2、@CachePut

项⽬运⾏中会对数据库的信息进⾏更新,如果仍然使⽤ @Cacheable 就会导致数据库的信息和缓存的信息不⼀致。在以往的项⽬中,⼀般更新完数据库后,再⼿动删除掉 Redis 中对应的缓存,以保证数据的⼀致性。Spring 提供了另外的⼀种解决⽅案,可以以优雅的⽅式去更新缓存。

与 @Cacheable 不同的是使⽤ @CachePut 标注的⽅法在执⾏前,不会去检查缓存中是否存在之前执⾏过的结果,⽽是每次都会执⾏该⽅法,并将执⾏结果以键值对的形式存⼊指定的缓存中。

@CachePut 配置⽅法:

  • value 缓存的名称。
  • key 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照⽅法的所有参数进⾏组合。
  • condition 缓存的条件,可以为空,使⽤ SpEL 编写,返回 true 或者 false,只有为 true 才进⾏缓存。


3.3、@CacheEvict

@CacheEvict 是⽤来标注在需要清除缓存元素的⽅法或类上的,当标记在⼀个类上时表示其中所有的⽅法的执⾏都会触发缓存的清除操作。@CacheEvict 可以指定的属性有 value、key、condition、allEntries 和beforeInvocation,其中 value、key 和 condition 的语义与 @Cacheable 对应的属性类似。即 value 表示清除操作是发⽣在哪些 Cache 上的(对应 Cache 的名称);key 表示需要清除的是哪个 key,如未指定则会使⽤默认策略⽣成的 key;condition 表示清除操作发⽣的条件。


3.4、总结

  • @Cacheable 作⽤和配置⽅法

主要针对⽅法配置,能够根据⽅法的请求参数对其结果进⾏缓存:

主要参数 解 释 举例
value 缓存的名称,在 spring 配置⽂件中定义,必须指定⾄少⼀个 如 @Cacheable(value="mycache")或者 @Cacheable(value={"cache1","cache2"}
key 缓存的 key,可以为空,如果指定要按照 SpEL表达式编写,如果不指定,则缺省按照⽅法的所有参数进⾏组合 如@Cacheable(value="testcache",key="#userName")
condition 缓存的条件,可以为空,使⽤ SpEL 编写,返回true 或者 false,只有为 true 才进⾏缓存 如@Cacheable(value="testcache",condition="#userName.length()>2")


  • @CachePut 作⽤和配置⽅法

@CachePut 的作⽤是主要针对⽅法配置,能够根据⽅法的请求参数对其结果进⾏缓存,和 @Cacheable 不同的是,它每次都会触发真实⽅法的调⽤。

主要参数 解释 举例
value 缓存的名称,在 spring 配置⽂件中定义,必须指定⾄少⼀个 如@Cacheable(value="mycache")或者 @Cacheable(value={"cache1","cache2"}
key 缓存的 key,可以为空,如果指定要按照 SpEL表达式编写,如果不指定,则缺省按照⽅法的所有参数进⾏组合 如@Cacheable(value="testcache",key="#userName")
condition 缓存的条件,可以为空,使⽤ SpEL 编写,返回true 或者 false,只有为 true 才进⾏缓存 如@Cacheable(value="testcache",condition="#userName.length()>2")


  • @CacheEvict 作⽤和配置⽅法

主要针对⽅法配置,能够根据⼀定的条件对缓存进⾏清空。

主要参数 解释 举例
value 缓存的名称,在 spring 配置⽂件中定义,必须指定⾄少⼀个 如 @CachEvict(value="mycache")或者 @CachEvict(value={"cache1","cache2"}
key 缓存的 key,可以为空,如果指定要按照SpEL 表达式编写,如果不指定,则缺省按照⽅法的所有参数进⾏组合 如@CachEvict(value="testcache",key="#userName")
condition 缓存的条件,可以为空,使⽤ SpEL 编写,返回 true 或者 false,只有为 true 才清空缓存 如@CachEvict(value="testcache",condition="#userName.length()>2")
allEntries 是否清空所有缓存内容,缺省为 false,如果指定为 true,则⽅法调⽤后将⽴即清空所有缓存 如@CachEvict(value="testcache",allEntries=true)
beforeInvocation 是否在⽅法执⾏前就清空,缺省为 false,如果指定为 true,则在⽅法还没有执⾏的时候就清空缓存,缺省情况下,如果⽅法执⾏抛出异常,则不会清空缓存 如@CachEvict(value="testcache",beforeInvocation=true)


4、Cache具体使用

4.1、实体类

定义一个User类,模拟对象存储:

public class User implements Serializable {
    private static final long serialVersionUID = 8655851615465363473L;
    private Long id;
    private String username;
    private String password;
    
    //省略getter、setter
}       


4.2、服务层


  • 接口:
public interface UserService {

    /**
     * 删除
     *
     * @param user 用户对象
     * @return 操作结果
     */
    User saveOrUpdate(User user);

    /**
     * 添加
     *
     * @param id key值
     * @return 返回结果
     */
    User get(Long id);

    /**
     * 删除
     *
     * @param id key值
     */
    void delete(Long id);
}


  • 实现类:

实现类里用到了最核心的三个注解:@Cacheable、@CachePut、@CacheEvict

@Service
public class UserServiceImpl implements UserService {
    private static final Map<Long, User> DATABASES = new HashMap<>();

    static {
        DATABASES.put(1L, new User(1L, "u1", "p1"));
        DATABASES.put(2L, new User(2L, "u2", "p2"));
        DATABASES.put(3L, new User(3L, "u3", "p3"));
    }


    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);

    @Cacheable(value = "user", key = "#id")
    @Override
    public User get(Long id) {
        // TODO 假设它是从数据库读取出来的
        log.info("进入 get 方法");
        return DATABASES.get(id);
    }

    @CachePut(value = "user", key = "#user.id")
    @Override
    public User saveOrUpdate(User user) {
        DATABASES.put(user.getId(), user);
        log.info("进入 saveOrUpdate 方法");
        return user;
    }

    @CacheEvict(value = "user", key = "#id")
    @Override
    public void delete(Long id) {
        DATABASES.remove(id);
        log.info("进入 delete 方法");
    }
}


4.4、启动类

启动类需要开启缓存配置@EnableCaching

@SpringBootApplication
@EnableCaching
public class SpringbootCacheRedisApplication {

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

}


4.5、测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class CacheRedisTest {

    private static final Logger log = LoggerFactory.getLogger(CacheRedisTest.class);


    @Autowired
    private UserService userService;


    @Test
    public void get() {
        final User user = userService.saveOrUpdate(new User(5L, "u5", "p5"));
        log.info("[saveOrUpdate] - [{}]", user);
        final User user1 = userService.get(5L);
        log.info("[get] - [{}]", user1);
        userService.delete(5L);
    }
}

运行结果:

在这里插入图片描述可以看到增删改查中,查询是没有日志输出的,因为它直接从缓存中获取的数据,而添加、修改、删除都是会进入方法内执行具体的业务代码,然后通过切面去删除掉Redis中的缓存数据。



参考:

【1】:《精通 Spring Boot 42 讲》
【2】:一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
【3】:《Spring Boot Vue全栈开发实战》
【4】:Spring Cache + Redis

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
28天前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
|
29天前
|
存储 缓存 NoSQL
【赵渝强老师】基于Redis的旁路缓存架构
本文介绍了引入缓存后的系统架构,通过缓存可以提升访问性能、降低网络拥堵、减轻服务负载和增强可扩展性。文中提供了相关图片和视频讲解,并讨论了数据库读写分离、分库分表等方法来减轻数据库压力。同时,文章也指出了缓存可能带来的复杂度增加、成本提高和数据一致性问题。
【赵渝强老师】基于Redis的旁路缓存架构
|
1月前
|
缓存 NoSQL Redis
Redis 缓存使用的实践
《Redis缓存最佳实践指南》涵盖缓存更新策略、缓存击穿防护、大key处理和性能优化。包括Cache Aside Pattern、Write Through、分布式锁、大key拆分和批量操作等技术,帮助你在项目中高效使用Redis缓存。
200 22
|
22天前
|
缓存 NoSQL PHP
Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出
本文深入探讨了Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出。文章还介绍了Redis在页面缓存、数据缓存和会话缓存等应用场景中的使用,并强调了缓存数据一致性、过期时间设置、容量控制和安全问题的重要性。
36 5
|
1月前
|
缓存 NoSQL 中间件
redis高并发缓存中间件总结!
本文档详细介绍了高并发缓存中间件Redis的原理、高级操作及其在电商架构中的应用。通过阿里云的角度,分析了Redis与架构的关系,并展示了无Redis和使用Redis缓存的架构图。文档还涵盖了Redis的基本特性、应用场景、安装部署步骤、配置文件详解、启动和关闭方法、systemctl管理脚本的生成以及日志警告处理等内容。适合初学者和有一定经验的技术人员参考学习。
168 7
|
1月前
|
存储 缓存 监控
利用 Redis 缓存特性避免缓存穿透的策略与方法
【10月更文挑战第23天】通过以上对利用 Redis 缓存特性避免缓存穿透的详细阐述,我们对这一策略有了更深入的理解。在实际应用中,我们需要根据具体情况灵活运用这些方法,并结合其他技术手段,共同保障系统的稳定和高效运行。同时,要不断关注 Redis 缓存特性的发展和变化,及时调整策略,以应对不断出现的新挑战。
64 10
|
1月前
|
缓存 监控 NoSQL
Redis 缓存穿透的检测方法与分析
【10月更文挑战第23天】通过以上对 Redis 缓存穿透检测方法的深入探讨,我们对如何及时发现和处理这一问题有了更全面的认识。在实际应用中,我们需要综合运用多种检测手段,并结合业务场景和实际情况进行分析,以确保能够准确、及时地检测到缓存穿透现象,并采取有效的措施加以解决。同时,要不断优化和改进检测方法,提高检测的准确性和效率,为系统的稳定运行提供有力保障。
50 5
|
7月前
|
NoSQL Java Redis
SpringBoot集成Redis解决表单重复提交接口幂等(亲测可用)
SpringBoot集成Redis解决表单重复提交接口幂等(亲测可用)
503 0
|
7月前
|
NoSQL Java Redis
SpringBoot集成Redis
SpringBoot集成Redis
499 0
|
7月前
|
NoSQL Java Redis
SpringBoot集成Redis并使用Redis发布订阅模式
SpringBoot集成Redis并使用Redis发布订阅模式
292 3