Spring Boot (五):Redis缓存使用姿势盘点
mhr18 2024-11-24 18:50 20 浏览 0 评论
如果我的文章对您有帮助,请关注支持下作者的公众号:极客挖掘机,获取最新干货推送:)
1. Redis 简介
Redis 是目前业界使用最广泛的内存数据存储。相比 Memcached,Redis 支持更丰富的数据结构,例如 hashes, lists, sets 等,同时支持数据持久化。除此之外,Redis 还提供一些类数据库的特性,比如事务,HA,主从库。可以说 Redis 兼具了缓存系统和数据库的一些特性,因此有着丰富的应用场景。本文介绍 Redis 在 Spring Boot 中两个典型的应用场景。
2. Lettuce 简介
如果在 Java 应用中使用过 Redis 缓存,那么对 Jedis 一定不陌生, Lettuce 和 Jedis 一样,都是连接 Redis Server 的客户端程序。Jedis 在实现上是直连 Redis Server,多线程环境下非线程安全,除非使用连接池,为每个 Jedis 实例增加物理连接。 Lettuce 基于 Netty 的连接实例(StatefulRedisConnection),可以在多个线程间并发访问,且线程安全,满足多线程环境下的并发访问,同时它是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。
3. Spring Boot 应用中使用方式
- 直接通过 RedisTemplate 来使用
- 使用 Spring Cache 集成 Redis
- 通过 Spring Session 做 Session 共享
4. 工程实战
4.1 工程依赖 pom.xml 如下:
代码清单:spring-boot-redis/pom.xml
<dependencies> <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-web</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
- spring-boot-starter-data-redis :在 Spring Boot 2.x 后底层不再是使用 Jedis ,而是换成了 Lettuce ,如图:
- commons-pool2 : 用作 redis 连接池,如不引入启动会报错。
- spring-session-data-redis : Spring Session 引入,用作共享 Session 。
4.2 配置文件 application.yml
代码清单:spring-boot-redis/src/main/resources/application.yml
server: port: 8080 servlet: session: timeout: 30m spring: application: name: spring-boot-redis cache: # 使用了Spring Cache后,能指定spring.cache.type就手动指定一下,虽然它会自动去适配已有Cache的依赖,但先后顺序会对Redis使用有影响(JCache -> EhCache -> Redis -> Guava) type: REDIS redis: host: 192.168.0.128 port: 6379 password: 123456 # 连接超时时间(ms) timeout: 10000 # Redis默认情况下有16个分片,这里配置具体使用的分片,默认是0 database: 0 lettuce: pool: # 连接池最大连接数(使用负值表示没有限制) 默认 8 max-active: 100 # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 max-wait: -1 # 连接池中的最大空闲连接 默认 8 max-idle: 8 # 连接池中的最小空闲连接 默认 0 min-idle: 0
这里的配置不多解释,需要解释的已经标注注释。
4.3 RedisTemplate 使用方式
4.3.1 创建实体类 User.java
代码清单:spring-boot-redis/src/main/java/com/springboot/springbootredis/model/User.java
@Data @AllArgsConstructor @NoArgsConstructor public class User implements Serializable { private static final long serialVersionUID = 662692455422902539L; private Long id; private String name; private int age; }
4.3.2 自定义 RedisTemplate
默认情况下的模板只能支持 RedisTemplate<String, String> ,也就是只能存入字符串,这在开发中是不友好的,所以自定义模板是很有必要的,当自定义了模板又想使用 String 存储这时候就可以使用 StringRedisTemplate 的方式,它们并不冲突,添加配置类 RedisCacheConfig.java ,代码如下:
代码清单:spring-boot-redis/src/main/java/com/springboot/springbootredis/config/RedisCacheConfig.java
@Configuration @AutoConfigureAfter(RedisAutoConfiguration.class) public class RedisCacheConfig { @Bean public RedisTemplate<String, Serializable> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) { RedisTemplate<String, Serializable> template = new RedisTemplate<>(); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setConnectionFactory(redisConnectionFactory); return template; } }
4.3.3 测试接口 UserController.java
代码清单:
@RestController @Slf4j public class UserController { @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired RedisTemplate<String, Serializable> redisCacheTemplate; @Autowired UserService userService; @GetMapping("/test") public void test() { stringRedisTemplate.opsForValue().set("geekdigging", "https://www.geekdigging.com/"); log.info("当前获取对象:{}",stringRedisTemplate.opsForValue().get("geekdigging")); redisCacheTemplate.opsForValue().set("geekdigging.com", new User(1L, "geekdigging", 18)); User user = (User) redisCacheTemplate.opsForValue().get("geekdigging.com"); log.info("当前获取对象:{}", user); } }
4.3.4 测试
启动服务,打开浏览器访问链接:http://localhost:8080/test ,查看控制台日志打印,如下:
2019-09-24 23:49:30.191 INFO 19108 --- [nio-8080-exec-1] c.s.s.controller.UserController : 当前获取对象:https://www.geekdigging.com/ 2019-09-24 23:49:30.243 INFO 19108 --- [nio-8080-exec-1] c.s.s.controller.UserController : 当前获取对象:User(id=1, name=geekdigging, age=18)
测试成功。
4.4 使用 Spring Cache 集成 Redis
4.4.1 Spring Cache 特点
Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 Redis),而是一个对缓存使用的抽象,通过在既有代码中添加少量它定义的各种 annotation,即能够达到缓存方法的返回对象的效果。
Spring Cache 具备相当的好的灵活性,不仅能够使用 SpEL(Spring Expression Language)来定义缓存的 key 和各种 condition,还提供开箱即用的缓存临时存储方案,也支持和主流的专业缓存例如 EHCache、Redis、Guava 的集成。
- 基于 annotation 即可使得现有代码支持缓存
- 开箱即用 Out-Of-The-Box,不用安装和部署额外第三方组件即可使用缓存
- 支持 Spring Express Language,能使用对象的任何属性或者方法来定义缓存的 key 和 condition
- 支持 AspectJ,并通过其实现任何方法的缓存支持
- 支持自定义 key 和自定义缓存管理者,具有相当的灵活性和扩展性
4.4.2 定义接口 UserService.java
代码清单:spring-boot-redis/src/main/java/com/springboot/springbootredis/service/UserService.java
public interface UserService { User save(User user); User get(Long id); void delete(Long id); }
4.4.3 接口实现 UserServiceImpl.java
代码清单:spring-boot-redis/src/main/java/com/springboot/springbootredis/service/impl/UserServiceImpl.java
@Service @Slf4j public class UserServiceImpl implements UserService { private static final Map<Long, User> USER_MAP = new HashMap<>(); static { USER_MAP.put(1L, new User(1L, "geekdigging.com", 18)); USER_MAP.put(2L, new User(2L, "geekdigging.com", 19)); USER_MAP.put(3L, new User(3L, "geekdigging.com", 20)); } @CachePut(value = "user", key = "#user.id") @Override public User save(User user) { USER_MAP.put(user.getId(), user); log.info("进入 save 方法,当前存储对象:{}", user); return user; } @Cacheable(value = "user", key = "#id") @Override public User get(Long id) { log.info("进入 get 方法,当前获取对象:{}", USER_MAP.get(id)); return USER_MAP.get(id); } @CacheEvict(value = "user", key = "#id") @Override public void delete(Long id) { USER_MAP.remove(id); log.info("进入 delete 方法,删除成功"); } }
为了方便演示数据库操作,直接定义了一个 Map<Long, User> USER_MAP ,这里的核心就是三个注解 @Cacheable 、 @CachePut 、 @CacheEvict 。
4.4.3.1 @Cacheable
根据方法的请求参数对其结果进行缓存
- key: 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合(如:@Cacheable(value="user",key="#userName"))
- value: 缓存的名称,必须指定至少一个(如:@Cacheable(value="user") 或者 @Cacheable(value={"user1","use2"}) )
- condition: 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存(如:@Cacheable(value = "user", key = "#id",condition = "#id < 10"))
4.4.3.2 @CachePut
根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用
- key: 同上
- value: 同上
- condition: 同上
4.4.3.3 @CachEvict
根据条件对缓存进行清空
- key: 同上
- value: 同上
- condition: 同上
- allEntries: 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存(如: @CacheEvict(value = "user", key = "#id", allEntries = true) )
- beforeInvocation: 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存(如: @CacheEvict(value = "user", key = "#id", beforeInvocation = true) )
4.4.4 启动主类
代码清单:spring-boot-redis/src/main/java/com/springboot/springbootredis/SpringBootRedisApplication.java
@SpringBootApplication @EnableCaching public class SpringBootRedisApplication { public static void main(String[] args) { SpringApplication.run(SpringBootRedisApplication.class, args); } }
- 这里需增加注解 @EnableCaching 开启 Spring Session。
4.4.5 增加测试接口
代码清单:spring-boot-redis/src/main/java/com/springboot/springbootredis/controller/UserController.java
@GetMapping("/test1") public void test1() { User user = userService.save(new User(4L, "geekdigging.com", 35)); log.info("当前 save 对象:{}", user); user = userService.get(1L); log.info("当前 get 对象:{}", user); userService.delete(5L); }
4.4.6 测试
启动服务,打开浏览器访问链接:http://localhost:8080/test ,刷新页面,控制台日志打印如下:
2019-09-25 00:07:21.887 INFO 21484 --- [nio-8080-exec-1] c.s.s.service.impl.UserServiceImpl : 进入 save 方法,当前存储对象:User(id=4, name=geekdigging.com, age=35) 2019-09-25 00:07:21.897 INFO 21484 --- [nio-8080-exec-1] c.s.s.controller.UserController : 当前 save 对象:User(id=4, name=geekdigging.com, age=35) 2019-09-25 00:07:21.899 INFO 21484 --- [nio-8080-exec-1] c.s.s.service.impl.UserServiceImpl : 进入 get 方法,当前获取对象:User(id=1, name=geekdigging.com, age=18) 2019-09-25 00:07:21.900 INFO 21484 --- [nio-8080-exec-1] c.s.s.controller.UserController : 当前 get 对象:User(id=1, name=geekdigging.com, age=18) 2019-09-25 00:07:21.901 INFO 21484 --- [nio-8080-exec-1] c.s.s.service.impl.UserServiceImpl : 进入 delete 方法,删除成功
再次刷新页面,查看控制台日志:
2019-09-25 00:08:54.076 INFO 21484 --- [nio-8080-exec-7] c.s.s.service.impl.UserServiceImpl : 进入 save 方法,当前存储对象:User(id=4, name=geekdigging.com, age=35) 2019-09-25 00:08:54.077 INFO 21484 --- [nio-8080-exec-7] c.s.s.controller.UserController : 当前 save 对象:User(id=4, name=geekdigging.com, age=35) 2019-09-25 00:08:54.079 INFO 21484 --- [nio-8080-exec-7] c.s.s.controller.UserController : 当前 get 对象:User(id=1, name=geekdigging.com, age=18) 2019-09-25 00:08:54.079 INFO 21484 --- [nio-8080-exec-7] c.s.s.service.impl.UserServiceImpl : 进入 delete 方法,删除成功
结果和我们期望的一致,可以看到增删改查中,查询是没有日志输出的,因为它直接从缓存中获取的数据,而添加、修改、删除都是会进入 UserServiceImpl 的方法内执行具体的业务代码。
4.5 Session 共享
4.5.1 Spring Session 简介
Spring Session 提供了一套创建和管理 Servlet HttpSession 的方案。Spring Session 提供了集群 Session(Clustered Sessions)功能,默认采用外置的 Redis 来存储 Session 数据,以此来解决 Session 共享的问题。
4.5.2 启动主类 SpringBootRedisApplication.java
代码清单:spring-boot-redis/src/main/java/com/springboot/springbootredis/SpringBootRedisApplication.java
@SpringBootApplication @EnableCaching @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800) public class SpringBootRedisApplication { public static void main(String[] args) { SpringApplication.run(SpringBootRedisApplication.class, args); } }
- maxInactiveIntervalInSeconds: 设置 Session 失效时间,使用 Spring Session 之后,原 Spring Boot 配置文件 application.yml 中的 server.session.timeout 属性不再生效。
4.5.3 增加测试接口
代码清单:spring-boot-redis/src/main/java/com/springboot/springbootredis/controller/UserController.java
@GetMapping("/getBlogUrl") public String getSessionId(HttpServletRequest request) { String url = (String) request.getSession().getAttribute("url"); if (StringUtils.isEmpty(url)) { request.getSession().setAttribute("url", "https://www.geekdigging.com/"); } log.info("获取session内容为: {}", request.getSession().getAttribute("url")); return request.getRequestedSessionId(); }
4.5.4 测试
启动服务,打开浏览器访问链接:http://localhost:8080/getBlogUrl ,查看 Redis 当前存储内容,如下图:
其中 1569339180000 为失效时间,意思是这个时间后 Session 失效, b2522824-1094-478e-a435-554a551bc8bb 为 SessionId 。
4.5.6 如何在多台服务中共享 Session
按照上面的步骤在另一个项目中再次配置一次,启动后自动就进行了 Session 共享。
5. 示例代码
示例代码-Github:https://github.com/meteor1993/spring-boot-examples/tree/master/spring-boot-redis
示例代码-Gitee:https://gitee.com/inwsy/spring-boot-examples/tree/master/spring-boot-redis
6. 参考
http://emacoo.cn/backend/spring-redis/
https://blog.battcn.com/2018/05/11/springboot/v2-nosql-redis/
相关推荐
- 【推荐】一个开源免费、AI 驱动的智能数据管理系统,支持多数据库
-
如果您对源码&技术感兴趣,请点赞+收藏+转发+关注,大家的支持是我分享最大的动力!!!.前言在当今数据驱动的时代,高效、智能地管理数据已成为企业和个人不可或缺的能力。为了满足这一需求,我们推出了这款开...
- Pure Storage推出统一数据管理云平台及新闪存阵列
-
PureStorage公司今日推出企业数据云(EnterpriseDataCloud),称其为组织在混合环境中存储、管理和使用数据方式的全面架构升级。该公司表示,EDC使组织能够在本地、云端和混...
- 对Java学习的10条建议(对java课程的建议)
-
不少Java的初学者一开始都是信心满满准备迎接挑战,但是经过一段时间的学习之后,多少都会碰到各种挫败,以下北风网就总结一些对于初学者非常有用的建议,希望能够给他们解决现实中的问题。Java编程的准备:...
- SQLShift 重大更新:Oracle→PostgreSQL 存储过程转换功能上线!
-
官网:https://sqlshift.cn/6月,SQLShift迎来重大版本更新!作为国内首个支持Oracle->OceanBase存储过程智能转换的工具,SQLShift在过去一...
- JDK21有没有什么稳定、简单又强势的特性?
-
佳未阿里云开发者2025年03月05日08:30浙江阿里妹导读这篇文章主要介绍了Java虚拟线程的发展及其在AJDK中的实现和优化。阅前声明:本文介绍的内容基于AJDK21.0.5[1]以及以上...
- 「松勤软件测试」网站总出现404 bug?总结8个原因,不信解决不了
-
在进行网站测试的时候,有没有碰到过网站崩溃,打不开,出现404错误等各种现象,如果你碰到了,那么恭喜你,你的网站出问题了,是什么原因导致网站出问题呢,根据松勤软件测试的总结如下:01数据库中的表空间不...
- Java面试题及答案最全总结(2025版)
-
大家好,我是Java面试陪考员最近很多小伙伴在忙着找工作,给大家整理了一份非常全面的Java面试题及答案。涉及的内容非常全面,包含:Spring、MySQL、JVM、Redis、Linux、Sprin...
- 数据库日常运维工作内容(数据库日常运维 工作内容)
-
#数据库日常运维工作包括哪些内容?#数据库日常运维工作是一个涵盖多个层面的综合性任务,以下是详细的分类和内容说明:一、数据库运维核心工作监控与告警性能监控:实时监控CPU、内存、I/O、连接数、锁等待...
- 分布式之系统底层原理(上)(底层分布式技术)
-
作者:allanpan,腾讯IEG高级后台工程师导言分布式事务是分布式系统必不可少的组成部分,基本上只要实现一个分布式系统就逃不开对分布式事务的支持。本文从分布式事务这个概念切入,尝试对分布式事务...
- oracle 死锁了怎么办?kill 进程 直接上干货
-
1、查看死锁是否存在selectusername,lockwait,status,machine,programfromv$sessionwheresidin(selectsession...
- SpringBoot 各种分页查询方式详解(全网最全)
-
一、分页查询基础概念与原理1.1什么是分页查询分页查询是指将大量数据分割成多个小块(页)进行展示的技术,它是现代Web应用中必不可少的功能。想象一下你去图书馆找书,如果所有书都堆在一张桌子上,你很难...
- 《战场兄弟》全事件攻略 一般事件合同事件红装及隐藏职业攻略
-
《战场兄弟》全事件攻略,一般事件合同事件红装及隐藏职业攻略。《战场兄弟》事件奖励,事件条件。《战场兄弟》是OverhypeStudios制作发行的一款由xcom和桌游为灵感来源,以中世纪、低魔奇幻为...
- LoadRunner(loadrunner录制不到脚本)
-
一、核心组件与工作流程LoadRunner性能测试工具-并发测试-正版软件下载-使用教程-价格-官方代理商的架构围绕三大核心组件构建,形成完整测试闭环:VirtualUserGenerator(...
- Redis数据类型介绍(redis 数据类型)
-
介绍Redis支持五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(sortedset:有序集合)。1、字符串类型概述1.1、数据类型Redis支持...
- RMAN备份监控及优化总结(rman备份原理)
-
今天主要介绍一下如何对RMAN备份监控及优化,这里就不讲rman备份的一些原理了,仅供参考。一、监控RMAN备份1、确定备份源与备份设备的最大速度从磁盘读的速度和磁带写的带度、备份的速度不可能超出这两...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle位图索引 (63)
- oracle批量插入数据 (62)
- oracle事务隔离级别 (53)
- oracle 空为0 (50)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)