SpringBoot集成Redis作数据缓存

本文涉及的产品
云数据库 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
目录
相关文章
|
14天前
|
缓存 NoSQL 关系型数据库
13- Redis和Mysql如何保证数据⼀致?
该内容讨论了保证Redis和MySQL数据一致性的几种策略。首先提到的两种方法存在不一致风险:先更新MySQL再更新Redis,或先删Redis再更新MySQL。第三种方案是通过MQ异步同步以达到最终一致性,适用于一致性要求较高的场景。项目中根据不同业务需求选择不同方案,如对一致性要求不高的情况不做处理,时效性数据设置过期时间,高一致性需求则使用MQ确保同步,最严格的情况可能涉及分布式事务(如Seata的TCC模式)。
38 6
|
14天前
|
NoSQL Redis
05- Redis的数据淘汰策略有哪些 ?
Redis 提供了 8 种数据淘汰策略:挥发性 LRU、LFU 和 TTL(针对有过期时间的数据),挥发性随机淘汰,以及全库的 LRU、LFU 随机淘汰,用于在内存不足时选择删除。另外,还有不淘汰策略(no-eviction),允许新写入操作报错而非删除数据。
95 1
|
22天前
|
NoSQL Java Redis
SpringBoot集成Redis解决表单重复提交接口幂等(亲测可用)
SpringBoot集成Redis解决表单重复提交接口幂等(亲测可用)
147 0
|
27天前
|
NoSQL Java Redis
SpringBoot集成Redis
SpringBoot集成Redis
270 0
|
21天前
|
NoSQL Redis
Redis事务:保证数据操作的一致性和可靠性
Redis事务:保证数据操作的一致性和可靠性
|
1天前
|
存储 缓存 运维
软件体系结构 - 缓存技术(5)Redis Cluster
【4月更文挑战第20天】软件体系结构 - 缓存技术(5)Redis Cluster
79 10
|
6天前
|
人工智能 前端开发 Java
Java语言开发的AI智慧导诊系统源码springboot+redis 3D互联网智导诊系统源码
智慧导诊解决盲目就诊问题,减轻分诊工作压力。降低挂错号比例,优化就诊流程,有效提高线上线下医疗机构接诊效率。可通过人体画像选择症状部位,了解对应病症信息和推荐就医科室。
88 10
|
9天前
|
缓存 NoSQL Java
使用Redis进行Java缓存策略设计
【4月更文挑战第16天】在高并发Java应用中,Redis作为缓存中间件提升性能。本文探讨如何使用Redis设计缓存策略。Redis是开源内存数据结构存储系统,支持多种数据结构。Java中常用Redis客户端有Jedis和Lettuce。缓存设计遵循一致性、失效、雪崩、穿透和预热原则。常见缓存模式包括Cache-Aside、Read-Through、Write-Through和Write-Behind。示例展示了使用Jedis实现Cache-Aside模式。优化策略包括分布式锁、缓存预热、随机过期时间、限流和降级,以应对缓存挑战。
|
16天前
|
存储 NoSQL 算法
redis数据持久化
redis数据持久化
|
16天前
|
存储 缓存 NoSQL
使用redis进行缓存加速
使用redis进行缓存加速
26 0

热门文章

最新文章