【Redis】Java操作Redis之SpringDataRedis
Redis 支持多种 Java 客户端,以下是一些常见的:
1. Jedis
- 特点 :
是 Redis 官方推荐的 Java 客户端,它提供了简单易用的 API,与 Redis 命令一一对应,可以直接使用 Redis 的各种基本操作,如字符串操作(set、get)、哈希操作(hset、hget)等。
比较轻量级,资源消耗相对较少,适合在对资源要求不高的场景下使用,并且性能较好。 - 优点 :
包装了一个完整的 Redis 协议,能够很好地支持 Redis 的数据类型和操作。
支持 Redis 的持久化、事务、发布 / 订阅等功能,能够满足基本的 Redis 使用需求。
社区活跃,文档和示例丰富,方便开发者学习和使用。 - 缺点 :
需要自己管理连接,没有自动重连机制,如果 Redis 服务器重启或网络中断,需要自己处理重连逻辑。
对于高并发场景,连接池配置不当可能会导致性能瓶颈,因为需要手动管理连接池的大小和连接的分配。 - 适用场景 :
适合 Redis 使用场景较为简单,且对资源消耗和性能要求不是特别苛刻的应用。
在一些小型项目或者对 Redis 操作不复杂的情况下,可以快速集成和使用。
2. Lettuce
- 特点 :
使用了异步、非阻塞的 I/O 操作,能够提供较高的性能,尤其是在高并发场景下表现出色。
支持 Redis 协议的各个版本,并且能够很好地与 Redis 集群模式和主从模式配合使用,方便在集群环境中进行 Redis 操作。 - 优点 :
异步设计使得在处理大量并发请求时能够更有效地利用系统资源,提高了程序的性能和响应速度。
支持响应式编程模型,可以很好地与 Spring 5 的响应式框架集成,适合构建响应式系统。
对 Redis 集群的支持较好,能够自动处理节点的添加、删除等集群变化情况。 - 缺点 :
学习曲线相对较陡,由于它是基于异步编程模型的,对于习惯了同步编程的开发者来说,可能需要一定的时间来适应。文档和示例相对 Jedis 和 Redisson 等客户端来说还不够完善,可能会在使用过程中一些遇到问题难以快速解决。 - 适用场景 :
主要适用于高并发场景,特别是在构建具有高性能要求的分布式系统时,能够充分发挥其异步非阻塞的优势。
当与 Spring 5 等响应式框架结合使用时,可以构建出更加高效、灵活的响应式应用。
3. Redisson
- 特点 :
是一个功能强大的 Redis Java 客户端,它在 Jedis 的基础上进行了封装,提供了更高层次的抽象,使得 Java 开发者可以像使用本地 Java 对象一样使用 Redis 数据结构。
支持分布式锁、事务、发布 / 订阅、Lua 脚本等 Redis 特性,并且实现了许多分布式场景下的解决方案,如分布式集合、分布式队列等。 - 优点 :
提供了丰富的分布式解决方案,使得在分布式系统中使用 Redis 变得更加方便和直观。
具有良好的文档和示例,易于上手,并且社区支持较好。支持自动重连和重试机制,能够更好地应对网络波动和 Redis 服务器重启等情况。 - 缺点 :
由于封装层次较多,可能会带来一定的性能开销,相比 Jedis 等底层客户端,性能稍差。
对于一些简单的 Redis 操作,使用 Redisson 可能会显得有些繁琐,因为它更侧重于分布式场景下的高级功能。 - 适用场景 :
适合在分布式系统中使用 Redis,尤其是在需要实现分布式锁、分布式队列等复杂分布式功能的场景下。当项目需要快速开发分布式相关功能,并且对性能要求不是极其苛刻时,Redisson 是一个不错的选择。
4.SpringDataRedis
Spring Data Redis
是 Spring 提供的一个框架,它让在 Spring 应用里用 Redis 变得超简单。它把底层的 Redis Java 客户端(比如 Jedis
、Lettuce
)给封装起来,给我们提供了一套统一、好用的 API。
- 提供了对不同Redis客户端的整合(Lettuce和Jedis)
- 提供了RedisTemplate统一API来操作Redis
- 支持Redis的发布订阅模型
- 支持Redis哨兵和Redis集群
- 支持基于Lettuce的响应式编程
- 支持基于JDK.JSON.字符串.Spring对象的数据序列化及反序列化
- 支持基于Redis的JDKCollection实现
SpringDataRedis
中提供了RedisTemplate
工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:
以下链接是Spring官方提供的Spring data Redis中文学习文档: https://springdoc.cn/spring-data-redis/
快速入门
SpringBoot已经提供了对SpringDataRedis的支持,使用非常简单:
1.RedisTemplate
1.1 导入pom坐标
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.7</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.heima</groupId><artifactId>redis-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>redis-demo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><!--redis依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--common-pool--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><!--Jackson依赖--><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</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><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>
1.2 application配置文件
spring:redis:host: 192.168.88.100 #这里写你配置的redis主机地址port: 6379 #默认6379password: 1234 #你的密码lettuce:pool:max-active: 8 #最大连接max-idle: 8 #最大空闲连接min-idle: 0 #最小空闲连接max-wait: 1000 #连接等待时间1000ms
1.3 代码测试
@SpringBootTest
public class RedisDemoApplicationTests {@Autowiredprivate RedisTemplate redisTemplate;@Testpublic void test() {redisTemplate.opsForValue().set("name", "zhangsan");System.out.println(redisTemplate.opsForValue().get("name"));}
总结
SpringDataRedis的使用步骤:
- 1、引入
spring-boot-starter-data-redis
依赖 - 2、在
application.yml
配置Redis信息 - 3、注入
RedisTemplate
2.1 自定义序列化方式(编写RedisConfig配置类)
RedisTemplate可以接收任意Object作为值写入Redis,但是写入前会把Object序列化为字节形式,Java默认是采用JDK序列化,得到的结果在图形化界面RESP中看是这样的:
Spring Data Redis
默认使用 JdkSerializationRedisSerializer
作为序列化器。它会将 Java 对象序列化为字节数组存储到 Redis 中。
这就导致了
- 可读性差:存储在 Redis 中的数据是二进制字节数组,难以直接阅读和理解
- 内存占用大:比存放的原始数据长度要大很多
我们可以自定义RedisTemplate的序列化方式,代码如下:
@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {//创建redisTemplate对象RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();//配置连接工厂redisTemplate.setConnectionFactory(redisConnectionFactory);//配置JSON序列化工具GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();//设置key的序列化redisTemplate.setKeySerializer(RedisSerializer.string());redisTemplate.setHashKeySerializer(RedisSerializer.string());//设置value的序列化redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);return redisTemplate;}
}
这里采用了JSON序列化
来代替默认的JDK序列化
方式。最终结果如图:
整体可读性有了很大提升,并且能将Java对象自动的序列化为JSON字符串,并且查询时能自动把JSON反序列化为Java对象。不过,其中记录了序列化时对应的class名称,目的是为了查询时实现自动反序列化。但这会带来额外的内存开销。
2. StringRedisTemplate
我们提到过,尽管JSON的序列化方式可以满足我们的需求,但依然存在一些问题,如图:
为了在反序列化时知道对象的类型,JSON序列化器会将类的class类型写入json结果中,存入Redis,会带来额外的内存开销。
为了减少内存的消耗,我们可以采用手动序列化的方式,换句话说,就是不借助默认的序列化器,而是我们Java程序员自己来控制序列化的动作,同时,我们只采用String的序列化器,这样,在存储value时,我们就在内存中就不用多存储数据了,从而节约我们的内存空间。
这种用法比较普遍,因此SpringDataRedis就提供了RedisTemplate的子类:StringRedisTemplate,它的key和value的序列化方式默认就是String方式。
省去了我们自定义RedisTemplate的序列化方式的步骤,而是直接使用:
@SpringBootTest
public class RedisStringTests {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Testpublic void test() {stringRedisTemplate.opsForValue().set("name", "zhangsan");System.out.println(stringRedisTemplate.opsForValue().get("name"));}//ObjectMapper使用前需要引入jackson-databind依赖包private static final ObjectMapper mapper = new ObjectMapper();@Testpublic void testSaveUser() throws JsonProcessingException {User user = new User("李四", 20);String json = mapper.writeValueAsString(user);//写入数据stringRedisTemplate.opsForValue().set("user",json);//读取数据String jsonUser = stringRedisTemplate.opsForValue().get("user");User user1 = mapper.readValue(jsonUser, User.class);System.out.println(user1);}@Testpublic void testSaveUser2(){User user = new User("李四", 20);//手动序列化String jsonStr = JSONUtil.toJsonStr(user);//写入数据stringRedisTemplate.opsForValue().set("user", jsonStr);//读取数据String jsonUser = stringRedisTemplate.opsForValue().get("user");//手动反序列化User user1 = JSONUtil.toBean(jsonStr, User.class);System.out.println(user1);}
}
以上我使用了Jackson
和hutool的JSONUtil
两种方式。
此时我们再来看一看存储的数据,我们就会发现那个class数据已经不在了,节约了我们的空间~
最后小总结:
RedisTemplate
的两种序列化实践方案:
-
方案一:
- 自定义
RedisTemplate
- 修改
RedisTemplate
的序列化器为GenericJackson2JsonRedisSerializer
- 自定义
-
方案二:
- 使用
StringRedisTemplate
- 写入Redis时,手动把对象序列化为JSON
- 读取Redis时,手动把读取到的JSON反序列化为Java对象
hash结构操作
- 使用
@SpringBootTest
class RedisStringTests {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Testvoid testHash() {//对应redis当中的 key | field | valuestringRedisTemplate.opsForHash().put("user", "name", "张三");stringRedisTemplate.opsForHash().put("user", "age", "18");//对应redis当中 HGETALL:获取一个hash类型的key中的所有的field和valueMap<Object, Object> entries = stringRedisTemplate.opsForHash().entries("user");System.out.println("entries = " + entries);}
}
好啦,redis在java当中的操作我们就简单演示到这里,至于其他api我们在实战篇再去体会和学习~