当前位置: 首页 > news >正文

RedisTemplate、StringRedisTemplate、序列化器配置

Lettuce和Jedis

RedisTemplate是SpringDataRedis中对JedisApi的高度封装,提供了Redis各种操作、 异常处理及序列化,支持发布订阅。

首先我们要知道SpringData是Spring中数据操作的模块,包括对各种数据库的集成,比如我们之前学过的Spring Data JDBC、JPA等,其中有一个模块叫做Spring Data Redis,而RedisTemplate就是其中提供操作Redis的通用模板

Spring Data Redis中提供了如下的内容:

1、对不同Redis客户端的整合(Lettuce和Jedis

2、提供了RedisTemplate统一API操作Redis

3、⽀持Redis订阅发布模型

4、⽀持Redis哨兵和集群

5、⽀持基于Lettuce的响应式编程(底层就是Netty

6、⽀持基于JDK、JSON、字符串、Spring对象的数据序列化、反序列化

使用Spring Data Redis需要引入RedisTemplate依赖和commons-pool连接池依赖,Jedis与RedisTemplate底层使用的连接池都是commons-pool2,所以需要导入它

<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>

这⾥我们可以看⼀下spring-boot-starter-data-redis底层,发现并没有引入Jedis

原因:

在SpringBoot 2.x版本以后,从原来的Jedis替换成了lettuce,所以2.x以后开始默认使用Lettuce作为Redis客户端,Lettuce客户端基于Netty的NIO框架实现,只需要维持单一的连接(非阻塞式IO)即可高校支持业务端并发请求。同时,Lettuce支持的特性更加全面,其性能表现并不逊于,甚至优于Jedis。

简单理解:

  • Jedis:
    采用的直连,多个线程操作不安全,如果想要避免线程安全问题,就需要使用JedisPool连接池,但是也会有一些线程过多等其他问题,类似于BIO(阻塞式IO)
  • Lettuce:
    底层采用Netty,实例可以在多个线程中进行共享,不存在线程安全问题!类似NIO

默认的RedisTemplate测试

@SpringBootTest
class RedisTestApplicationTests { @Autowiredprivate RedisTemplate redisTemplate;@Testvoid contextLoads() {redisTemplate.opsForValue().set("CSDN","青秋.");System.out.println(redisTemplate.opsForValue().get("CSDN"));}
}

通过指令来查看发现是乱码,这就涉及到了序列化的问题了。

想要解决以上问题,需要了解RedisTemplate序列化的问题,首先进入RedisTemplate源码,发现需要设置key、hashKey和value、hashValue的序列化器

再往下看有一个默认的序列化器

也就是说,RedisTemplate默认采用的是默认的JDK序列化器,这种序列化方式会有一定的问题 比如可读性差、内存占用大

所以总结来说,我们可以修改key和value的RedisSerializer具体实现,这⾥我们可以先看⼀下 RedisSerializer的实现类有哪些:

  • JacksonJsonRedisSerializer: 序列化object对象为json字符串
  • Jackson2JsonRedisSerializer: 跟JacksonJsonRedisSerializer实际上是一样的
  • JdkSerializationRedisSerializer: 序列化java对象
  • GenericToStringSerializer: 可以将任何对象泛化为字符串并序列化
  • StringRedisSerializer: 简单的字符串序列化

各个序列化器性能测试对比

 @Testpublic void testSerial(){UserPO userPO = new UserPO(1111L,"小明_testRedis1",25);List<Object> list = new ArrayList<>();for(int i=0;i<200;i++){list.add(userPO);}JdkSerializationRedisSerializer j = new JdkSerializationRedisSerializer();GenericJackson2JsonRedisSerializer g = new GenericJackson2JsonRedisSerializer();Jackson2JsonRedisSerializer j2 = new Jackson2JsonRedisSerializer(List.class);Long j_s_start = System.currentTimeMillis();byte[] bytesJ = j.serialize(list);System.out.println("JdkSerializationRedisSerializer序列化时间:"+(System.currentTimeMillis()-j_s_start) + "ms,序列化后的长度:" + bytesJ.length);Long j_d_start = System.currentTimeMillis();j.deserialize(bytesJ);System.out.println("JdkSerializationRedisSerializer反序列化时间:"+(System.currentTimeMillis()-j_d_start));Long g_s_start = System.currentTimeMillis();byte[] bytesG = g.serialize(list);System.out.println("GenericJackson2JsonRedisSerializer序列化时间:"+(System.currentTimeMillis()-g_s_start) + "ms,序列化后的长度:" + bytesG.length);Long g_d_start = System.currentTimeMillis();g.deserialize(bytesG);System.out.println("GenericJackson2JsonRedisSerializer反序列化时间:"+(System.currentTimeMillis()-g_d_start));Long j2_s_start = System.currentTimeMillis();byte[] bytesJ2 = j2.serialize(list);System.out.println("Jackson2JsonRedisSerializer序列化时间:"+(System.currentTimeMillis()-j2_s_start) + "ms,序列化后的长度:" + bytesJ2.length);Long j2_d_start = System.currentTimeMillis();j2.deserialize(bytesJ2);System.out.println("Jackson2JsonRedisSerializer反序列化时间:"+(System.currentTimeMillis()-j2_d_start));}

测试结果

JdkSerializationRedisSerializer序列化时间:8ms,序列化后的长度:1325
JdkSerializationRedisSerializer反序列化时间:4
GenericJackson2JsonRedisSerializer序列化时间:52ms,序列化后的长度:17425
GenericJackson2JsonRedisSerializer反序列化时间:60
Jackson2JsonRedisSerializer序列化时间:4ms,序列化后的长度:9801
Jackson2JsonRedisSerializer反序列化时间:4
  • JdkSerializationRedisSerializer序列化后长度最小,Jackson2JsonRedisSerializer效率最高。
  • 如果综合考虑效率和可读性,牺牲部分空间,推荐key使用StringRedisSerializer,保持的key简明易读;value可以使用Jackson2JsonRedisSerializer
  • 如果空间比较敏感,效率要求不高,推荐key使用StringRedisSerializer,保持的key简明易读;value可以使用JdkSerializationRedisSerializer

自定义RedisTemplate

新建RedisConfig配置类,以下是固定模板,可以直接用

这个模板我们采用的Json序列化Value,String序列化Key

@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String,Object>
redisTemplate(RedisConnectionFactory factory){// 为了研发⽅便 key直接为String类型RedisTemplate<String,Object> template = new RedisTemplate<>();// 设置连接⼯⼚template.setConnectionFactory(factory);//设置key序列化 用的string序列化template.setKeySerializer(RedisSerializer.string());template.setHashKeySerializer(RedisSerializer.string());//序列化配置,通过JSON解析任意对象GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();//设置value序列化,采用的是Json序列化方式template.setValueSerializer(jsonRedisSerializer);template.setHashKeySerializer(jsonRedisSerializer);template.afterPropertiesSet();return template;}
}

Json序列化Value + RedisTemplate测试

@SpringBootTest
class RedisTestApplicationTests {@Autowiredprivate RedisTemplate<String,Object> redisTemplate;@Testvoid contextLoads() {redisTemplate.opsForValue().set("CSDN","青秋.");System.out.println(redisTemplate.opsForValue().get("CSDN"));}
}

此时用keys * 查看,没有乱码。那么再储存一个对象试试!

@Test
void saveUser(){redisTemplate.opsForValue().set("stringredistemplate",new User("Mask",20));System.out.println(redisTemplate.opsForValue().get("stringredistemplate"));
}

用可视化工具查看,发现JSON序列化Value后多了个@Class字段

虽然实现了对象的序列化和反序列化,但这是因为添加了@class字段,会导致额外的内存开销,在数据量特别大的时候就会有影响,但是如果没有@class就不会实现自动序列化和反序列化

实际开发中,如果为了节省空间,并不会完全使用JSON序列化来处理value, 而是统一采用String序列化器,储存Java对象也是如此,这就意味着我们需要重新编写RedisTemplate,但是SpringBoot其实提供了一个String序列化器实现的StringRedisTemplate,通过它可以完成以上的需求。

String序列化Value + StringRedisTemplate测试 

@SpringBootTest
class RedisTestApplicationTests {@Autowiredprivate StringRedisTemplate stringRedisTemplate;//Json⼯具private ObjectMapper mapper = new ObjectMapper();@Testvoid StringTemplate() throws JsonProcessingException {User user = new User("青秋",18);//⼿动序列化String json = mapper.writeValueAsString(user);//写⼊数据stringRedisTemplate.opsForValue().set("stringredistemplate",json);//读取数据String val =
stringRedisTemplate.opsForValue().get("stringredistemplate");//反序列化User u = mapper.readValue(val,User.class);System.out.println(u);}
}

总结

  • RedisTemplate的Key和Value的序列化器可以根据需要分别设置。
  • RedisTemplate默认使用JdkSerializationRedisSerializer存入数据,会将数据先序列化成字节数组然后在存入Redis数据库,这种value不可读。
  • 如果数据是Object类型,取出的时候又不想做任何的数据转换直接从Redis里面取出一个对象,那么使用RedisTemplate是更好的选择。
  • 当然任何情况下从Redis获取数据的时候,都会默认将数据当做字节数组转化,这样就会导致一个问题:当需要获取的数据不是以字节数组存在redis当中,而是正常的可读的字符串的时候,RedisTemplate就无法获取数据,获取到的值是NULL。这时就需要用StringRedisTempate或者专门设置RedisTemplate的序列化器!

 

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Django REST Framework(十四)路由Routes
  • 二十四、【机器学习】【非监督学习】- 高斯混合模型 (Gaussian Mixture Models, GMM)
  • 深入理解 Redis 的使用与监控
  • 移动UI:排行榜单页面如何设计,从这五点入手,附示例。
  • 【DP】01背包
  • Linux嵌入书学习—数据结构——栈(seqstak)
  • 鸿蒙(HarmonyOS)下拉选择控件
  • CSS实现表格无限轮播
  • Kafka基础概念
  • @NotNull、@NotEmpty 和 @NotBlank 区别
  • 【leetcode 详解】生成特殊数字的最少操作【中等】(C++思路精析)
  • C#中实现Web API的签名验证
  • 24种设计模式介绍与6大设计原则(电子版教程)
  • [Javascript】前端面试基础3【每日学习并更新10】
  • 【iOS】——Block循环引用
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • 4. 路由到控制器 - Laravel从零开始教程
  • ES6语法详解(一)
  • gitlab-ci配置详解(一)
  • MySQL几个简单SQL的优化
  • Object.assign方法不能实现深复制
  • Service Worker
  • windows-nginx-https-本地配置
  • 从setTimeout-setInterval看JS线程
  • 回顾 Swift 多平台移植进度 #2
  • 爬虫模拟登陆 SegmentFault
  • 排序算法之--选择排序
  • 前端面试总结(at, md)
  • 译米田引理
  • 用mpvue开发微信小程序
  • 进程与线程(三)——进程/线程间通信
  • (2024)docker-compose实战 (9)部署多项目环境(LAMP+react+vue+redis+mysql+nginx)
  • (2024,Flag-DiT,文本引导的多模态生成,SR,统一的标记化,RoPE、RMSNorm 和流匹配)Lumina-T2X
  • (2024,RWKV-5/6,RNN,矩阵值注意力状态,数据依赖线性插值,LoRA,多语言分词器)Eagle 和 Finch
  • (55)MOS管专题--->(10)MOS管的封装
  • (附源码)springboot家庭财务分析系统 毕业设计641323
  • (附源码)ssm本科教学合格评估管理系统 毕业设计 180916
  • (介绍与使用)物联网NodeMCUESP8266(ESP-12F)连接新版onenet mqtt协议实现上传数据(温湿度)和下发指令(控制LED灯)
  • (四)c52学习之旅-流水LED灯
  • (五)网络优化与超参数选择--九五小庞
  • (转)Linq学习笔记
  • .NET Remoting学习笔记(三)信道
  • .NET 应用架构指导 V2 学习笔记(一) 软件架构的关键原则
  • .Net下使用 Geb.Video.FFMPEG 操作视频文件
  • .sdf和.msp文件读取
  • .sys文件乱码_python vscode输出乱码
  • @kafkalistener消费不到消息_消息队列对战之RabbitMq 大战 kafka
  • @ModelAttribute 注解
  • @RequestMapping处理请求异常
  • @ResponseBody
  • [ Socket学习 ] 第一章:网络基础知识
  • [C++] Windows中字符串函数的种类
  • [C++] 深入理解面向对象编程特性 : 继承
  • [C++]拼图游戏