RedisTemplate、StringRedisTemplate、序列化器配置

news2024/9/20 5:41:56

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 { 
 @Autowired
 private RedisTemplate redisTemplate;
 @Test
 void 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: 简单的字符串序列化

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

 @Test
    public 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 {
 @Bean
 public 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 {
 @Autowired
 private RedisTemplate<String,Object> redisTemplate;
 @Test
 void 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 {

 @Autowired
 private StringRedisTemplate stringRedisTemplate;

 //Json⼯具
 private ObjectMapper mapper = new ObjectMapper();
 @Test
 void 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的序列化器!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1957512.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot轻松实现多数据源切换

一.需求背景 项目需要实现在多个数据源之间读写数据&#xff0c;例如在 A 数据源和 B 数据源读取数据&#xff0c;然后在 C 数据源写入数据 或者 部分业务数据从 A 数据源中读取、部分从B数据源中读取诸如此类需求。本文将简单模拟在SpringBoot项目中实现不同数据源之间读取数…

数据结构:二分图以及判定二分图

文章目录 一、二分图的基本知识1、特性2、图示3、检查一个图是否为二分图3.1、着色的算法原理和思路3.2、算法示例&#xff1a;使用 BFS 检查二分图3.3、算法示例&#xff1a;使用 DFS 检查二分图 4、应用 二、例题1.LeetCode&#xff1a;785. 判断二分图2.Acwing&#xff1a;8…

软件开发中UML的基本概念与UML类图中存在哪些关系与如何绘制?

UML的概述 UML在维基百科中是这样子描述的 可以使用的工具这里网上说的有些花哨,我自己一般使用processon,里面的UML相关功能就很全。 UML类图的相关概念解释 解释一下什么是类图? 定义系统中的类,描述系统中类的属性和方法,就是描述类的内部结构,表示类与类之间的关…

手摸手教你撕碎西门子S7通讯协议04--S7COMM请求

1、S7通讯回顾 - &#xff08;1&#xff09;建立TCP连接 Socket.Connect-》已实现 - &#xff08;2&#xff09;发送访问请求 COTP-》已实现 - &#xff08;3&#xff09;交换通信信息 Setup Communication-》本节实现 - &#xff08;4&#xff09;执行相关操作 …

诱骗IoT恶意软件跟踪CC服务器

工作背景 在分析 IoT 僵尸网络时&#xff0c;识别C&C 服务器至关重要。C&C 服务器的 IP 地址一直都是商业威胁情报的重要组成部分&#xff0c;由于 C&C 服务器通信协议日渐复杂并且活跃周期较短&#xff0c;时效性和准确性也非常重要。如果可以自动化识别 IoT 恶意…

深度学习实战笔记3循环神经网络实现

我们要训练一个基于循环神经网络的字符级语言模型&#xff0c;根据用户提供的文本的前缀生成后续文本。 import math import torch from torch import nn from torch.nn import functional as F from d2l import torch as d2l batch_size, num_steps 32, 35 train_iter, voc…

防震减灾知识竞赛的规则和流程方案

防震减灾知识竞赛的规则主要包括赛制、比赛形式、参赛对象、竞赛内容等方面。 赛制&#xff1a;通常包括选拔赛、分区预赛和全国决赛三个阶段。选拔赛可以根据地区实际情况选择合适的组织形式&#xff0c;预赛和决赛则以现场比赛形式进行&#xff0c;由主办单位统一组织。 比…

掌握 Spring Boot + MyBatis-Plus 动态数据源切换,只要5分钟!

数据量猛增&#xff0c;通过动态数据源切换&#xff0c;我们不仅能提高查询效率&#xff0c;还能保证系统的高可用性。 通过将写操作集中在主库&#xff0c;读操作分散到多个从库&#xff0c;可以有效减轻数据库的压力。 在pom.xml中添加以下依赖&#xff1a; xml <depend…

记录|LabVIEW从0开始

目录 前言一、表达式节点和公式节点二、脚本与公式2.1 公式 三、Excel表格3.1 位置3.2 案例&#xff1a;波形值存入Excel表中3.3 案例&#xff1a;行写入&#xff0c;列写入 四、时间格式化4.1 获取当前时间4.2 对当前时间进行格式化 更新时间 前言 参考视频&#xff1a; LabVI…

hadoop学习(二)

一.MapReduce 1.1定义&#xff1a;是一个分布式运算程序的编程框架 1.2核心功能&#xff1a;将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序&#xff0c;并发运行在一个Hadoop集群上。 1.3优点 1&#xff09;易于编程 它简单的实现一些接口&#…

从入门到精通:电商设计师的职业发展指南

在当今数字时代&#xff0c;电商设计师的作用越来越重要。从电子商务网站的整体造型设计到产品页面的具体布局&#xff0c;他们的工作范围是电子商务企业成功的关键因素之一。然而&#xff0c;并不是每个人都对这个职业有深刻的理解。因此&#xff0c;在本文中&#xff0c;我们…

web3d值得学习并长期发展,性价比高吗?

在数字化浪潮日益汹涌的今天&#xff0c;Web3D技术以其独特的魅力和广泛的应用前景&#xff0c;逐渐成为技术领域的焦点。对于许多热衷于技术探索和创新的人来说&#xff0c;学习并长期发展Web3D技术无疑是一个值得考虑的选择。那么&#xff0c;Web3D技术的学习和发展究竟是否性…

华为openEuler 24.03 LTS系统安装系统后用SSH工具不能连接

sudo dnf install vim #安装VIM vim /etc/ssh/sshd_config #修改文件如图systemctl restart sshd #重启服务再用SSH工具连接成功

【树莓派+OpenCV+STM32】智能小车巡线_提取线路数据并通过串口通信传输

一、所用材料 树莓派4B树莓派官方摄像头STM32F103C8T6最小系统板 二、实现功能 在树莓派上用OpenCV对摄像头中的图像进行处理&#xff0c;将图像处理后的数据通过串口通信给到下位机STM32F103C8T6&#xff0c;再由下位机给出控制信号&#xff0c;利用pid算法实现对小车运动轨…

备忘录系统

目录 一、 系统简介 1.简介 2需求分析 3 编程环境与工具 二、 系统总体设计 1 系统的功能模块图。 2 各功能模块简介 3项目结构 4 三、 主要业务流程 &#xff08;1&#xff09;用户及管理员登录流程图 &#xff08;2&#xff09;信息添加流程 &#xff0…

强烈推荐这三款IOS应用,让你的生活更美好

Dino记账 Dino记账是一款结合了简洁设计和强大功能的记账应用&#xff0c;它通过多维度图表帮助用户轻松掌握金钱流向。应用界面明亮且配色突出&#xff0c;使得记录内容易于阅读&#xff0c;让记账和管理账目变得更加简单。 主要特性&#xff1a; 极简风格与易用性&#xff1…

史上最全Spring的@Transactional不生效的12大场景

一、事务不生效 1、访问权限的问题 在Spring框架中&#xff0c;AbstractFallbackTransactionAttributeSource是用于确定一个给定的方法是否应该被事务管理的一个抽象类。它的computeTransactionAttribute方法用于计算并返回一个方法的TransactionAttribute。computeTransacti…

SD NAND存储卡:小尺寸下的大容量存储

SD NAND是一种基于NAND闪存技术的存储设备&#xff0c;采用SD卡形式&#xff0c;具备高存储容量、高速度和高可靠性的特点&#xff0c;广泛应用于嵌入式系统和消费电子产品中。 在如今数据驱动的世界&#xff0c;存储技术的发展至关重要。MK米客方德作为存储芯片行业的领先者&…

C++(week14): C++提高:(一)面向对象设计:设计原则、设计模式

文章目录 一、面向对象设计的概念4.统一建模语言&#xff1a;UML语言StartUML 二、类与类之间的关系0.总结(1)类与类的五种关系(2)区别(3)面向对象 vs 基于对象 1.继承 (泛化耦合)2.组合 (Composition)3.聚合 (Aggregation)4.关联(1)双向关联(2)单向关联 5.依赖 (Dependency) 三…

JNDI注入-RMI和Reference

参考博客&#xff1a; JNDI注入与动态类加载 JNDI Java命名和接口目录为用Java编程语言编写的应用程序提供命名和目录功能。 可以通过一种通用方式访问各种服务&#xff0c;类似通过名字查找对象的功能&#xff0c;和RMI有点类似。 原生JNDI支持RMI&#xff0c;LDAP&#…