《知识点扫盲 · Redis 序列化器》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍

文章目录

    • 写在前面的话
    • 缓存序列化器
      • 技术简介
      • RedisSerializer 接口
      • 常用序列化器
      • 方案比较
      • 使用示例
      • 故障说明
      • 模拟异常
    • 总结陈词

CSDN.gif

写在前面的话

博主所在公司近期线上环境,某天遇到某服务大批量异常,查看异常堆栈指向 StringRedisSerializer#serialize,具体错误如下:java.lang.ClassCastException: class com.alibaba.fastjson.JSONArray cannot be cast to class java.lang.String
经排查发现,问题起因是该服务的开发人员,在某段业务代码修改了 redisTemplate 的序列化器导致该服务全局操作缓存异常,在解决问题并故障复盘后,这边也分享一下缓存序列化器的一些知识。

Tips:近期在更新程序猿入职必会系列(还在进行中),先更换一个知识点,调剂一下,每天都有新东西。


缓存序列化器

技术简介

Redis 数据序列化器,用于将数据在存储到 Redis 中时进行序列化(编码)和反序列化(解码)。
通俗来说,存数据的时候,需要先进行序列化,例如 Java 对象转为 JSON 字符串,再存到 Redis。
反过来,取数据的时候,需要先进行反序列化,例如 JSON 字符串转为 Java 对象,得到结果。

Tips:不一定是 Java 对象和 JSON 字符串的互相转换,也可以是和字节码、XML等多种格式。


RedisSerializer 接口

Spring-Redis 把上述逻辑封闭在redis序列化器中,接口为 RedisSerializer,完整类路径:

org.springframework.data.redis.serializer.RedisSerializer

该接口内容主要如下,主要关注序列化 serialize 和反序列化 deserialize 两个接口;

//**把对象序列化为byte数组
byte[] serialize(@Nullable T t) throws SerializationException;//**把byte数组反序列化为对象
T deserialize(@Nullable byte[] bytes) throws SerializationException;//**静态方法,java格式序列化器
static RedisSerializer<Object> java() {return java(null);
}//**静态方法,java格式序列化器
static RedisSerializer<Object> java(@Nullable ClassLoader classLoader) {return new JdkSerializationRedisSerializer(classLoader);
}//**静态方法,json格式序列化器
static RedisSerializer<Object> json() {return new GenericJackson2JsonRedisSerializer();
}//**静态方法,string数据类型序列化器
static RedisSerializer<String> string() {return StringRedisSerializer.UTF_8;
}//**静态方法,byte数据类型序列化器
static RedisSerializer<byte[]> byteArray() {return ByteArrayRedisSerializer.INSTANCE;
}//**检查是此序列化器否能够序列化指定数据类型
default boolean canSerialize(Class<?> type) {return ClassUtils.isAssignable(getTargetType(), type);
}//**此序列化器针对的数据类型,默认object
default Class<?> getTargetType() {return Object.class;
}

常用序列化器

常用的序列化器,包含但不限于如下:

  • JdkSerializationRedisSerializer
  • StringRedisSerializer
  • JacksonJsonRedisSerializer
  • GenericFastJsonRedisSerializer

另外按用途序列化器又分两种,Key 和 Value的,用代码表示比较清楚:

redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(jdkSerializationRedisSerializer);

Tips:一般 Key 都采用 StringRedisSerializer,Value 才需要讨论使用哪种策略。


方案比较

这四种 Redis 序列化器各有其优势和劣势,适用于不同的场景。
1、JdkSerializationRedisSerializer
优势:默认方案,不需要特别配置;可以序列化 Java 的任何对象,只要这些对象实现了 Serializable 接口。
劣势:性能相对较慢,序列化后的数据是二进制格式,可读性差。
过程:序列化是利用 ByteArrayOutputStream 将 Java 对象转换为 byte,反序列化就不赘述了。
2、StringRedisSerializer
优势:简单高效,适合处理字符串数据,序列化和反序列化速度快,序列化后的数据是字符串格式,易于调试和查看。
劣势:仅支持字符串类型,不适合复杂对象,在读取时需要手动转换为正确的类型,容易出错。
过程:序列化是使用指定的字符集直接把 String 类型转换为 byte。
3、JacksonJsonRedisSerializer
优势:序列化后的数据是 JSON 格式,易于调试和查看,兼容性强:支持多种 Java 对象,能够处理复杂对象和集合类型,也支持不同的配置选项,可以自定义序列化和反序列化过程。
劣势:需要引入 Jackson 库,增加了项目的依赖,速度低于 GenericFastJsonRedisSerializer。
过程:序列化时使用 jackson#ObjectMapper 把对象转换为 JSON 字符串,再转换为 byte。
4、GenericFastJsonRedisSerializer
优势:基于 FastJSON 实现,序列化和反序列化速度较快,适合处理大数据量,同时序列化后的数据为 JSON 格式,易于查看和调试,也支持泛型,能够处理不同类型的对象。
劣势:需要引入 FastJSON 库,增加了项目的依赖。
过程:序列化时使用 fastjson的JSON.toJSONBytes 把对象转换为 JSON 字符串,再转换为 byte。


使用示例

使用 JdkSerializationRedisSerializer
博主所在公司的旧框架封装的RedisTemplate采用JdkSerializationRedisSerializer作为值序列化器。
它的特点就是存的值如下所示,通过缓存客户端工具,难以阅读和直接修改。
image.png

使用 GenericFastJsonRedisSerializer
博主所在公司最新框架封装的RedisTemplate采用GenericFastJsonRedisSerializer作为值序列化器。
采用它的主要目的应该是解决可读性差的问题,如下所示,同时性能和兼容性方面都不错。
当存入的是一个对象,能将Java对象自动的序列化为JSON字符串,并且查询时能自动把JSON反序列化为Java对象。
image.png
image.png

手动序列化
这种方案仅供参考,就是自己用代码实现序列化和反序列化,就挺麻烦的。

private static final ObjectMapper mapper = new ObjectMapper();@Resource
private StringRedisTemplate stringRedisTemplate;@Test
void testSaveUser() throws JsonProcessingException {// 创建对象Student student = new Student("李白",28);// 手动序列化String json = mapper.writeValueAsString(student);// 写入数据stringRedisTemplate.opsForValue().set("student", json);// 获取数据String jsonUser = stringRedisTemplate.opsForValue().get("user:200");// 手动反序列化Student user1 = mapper.readValue(jsonUser, Student.class);System.out.println("user1 = " + user1);
}

故障说明

开发人员在新框架公用服务,编写了如下代码,凭一己之力修改了全局的 redisTemplate 的缓存策略。
最终可能实现了功能,但却本末倒置、因小失大了,替换回了不太合适的StringRedisSerializer,也带来了更多的影响。

@Transactional
public List<Map<String,String>> getSCHData(BasicDict dict) throws Exception {redisTemplate.setHashValueSerializer(new StringRedisSerializer());。。。省略业务代码
}

模拟异常

参考下方代码,可以看到,通过 setValueSerializer 修改序列化器之后,再次操作设置缓存的语句,就出现了异常。

try {ResultVO a = new ResultVO();a.setMessage("message");a.setCode("123");redisTemplate.opsForValue().set("PARAMS:PORTAL:0", a);Object o = redisTemplate.opsForValue().get("PARAMS:PORTAL:0");System.out.println(o);redisTemplate.setValueSerializer(new StringRedisSerializer());Object o2 = redisTemplate.opsForValue().get("PARAMS:PORTAL:0");//这句出现异常redisTemplate.opsForValue().set("PARAMS:PORTAL:0", a);
} catch (Exception e) {//异常信息如下://java.lang.ClassCastException: class com.zoe.onelink.common.entity.ResultVO cannot be cast to class java.lang.String (com.zoe.onelink.common.entity.ResultVO is in unnamed module of loader 'app'; java.lang.String is in module java.base of loader 'bootstrap')//at org.springframework.data.redis.serializer.StringRedisSerializer.serialize(StringRedisSerializer.java:36)e.printStackTrace();
}

image.png


总结陈词

此篇文章介绍了@Value在项目中得常见用法,仅供学习参考。
💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。

CSDN_END.gif

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

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

相关文章

米家护眼台灯怎么样?书客、米家、明基三款护眼台灯大PK

市面上出现的护眼台灯款式不得不说真的很多&#xff0c;大家若是想要在护眼台灯这个大市场里选购到一款性价比高、质量过关、口碑好且还真的实用的护眼台灯需要认真做好攻略。所以&#xff0c;我们要有技巧的对这些台灯进行筛选&#xff0c;避开那些三无的、网红品牌、无知名度…

http协议与nginx

动态页面与静态页面的差别&#xff1a; &#xff08;1&#xff09;URL不同 静态⻚⾯链接⾥没有“?” 动态⻚⾯链接⾥包含“&#xff1f;” &#xff08;2&#xff09;后缀不同 (开发语⾔不同) 静态⻚⾯⼀般以 .html .htm .xml 为后缀 动态⻚⾯⼀般以 .php .jsp .py等为后…

【吊打面试官系列-Dubbo面试题】Dubbo SPI 和 Java SPI 区别?

大家好&#xff0c;我是锋哥。今天分享关于 【Dubbo SPI 和 Java SPI 区别&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; Dubbo SPI 和 Java SPI 区别&#xff1f; JDK SPI JDK 标准的 SPI 会一次性加载所有的扩展实现&#xff0c;如果有的扩展吃实话很耗时&…

Python中的类型注解和静态类型检查使用详解

概要 Python作为一种动态类型语言,其灵活性和易用性使其广受欢迎。然而,动态类型也带来了一些问题,如代码可读性差和运行时错误等。为了提高代码质量和可维护性,Python从3.5版本开始引入了类型注解(Type Hints),并且借助第三方工具可以实现静态类型检查。本文将详细介绍…

Python学生信息管理系统

一、需求分析 学生管理系统应具备的功能 1、添加学生及成绩信息 2、将学生信息保存到文件中 3、修改和删除学生信息 4、查询学生信息 5、根据学生成绩进行排序 6、统计学生的总分 二、系统设计 2.1、学生信息管理系统的系统功能结构&#xff08;7大模块&#xff09; 1、录入…

vue里给img的src绑定数据失效

起因 在v-for遍历数据时想要通过给img的src单向绑定 图片路径时出现问题 解决过程 上网查说是webpack构建时识别不到&#xff0c;直接不单绑数据&#xff0c;写死试试看 解决方案 直接require导入图像文件模块

AI Agent调研--7种Agent框架对比!盘点国内一站式Agent搭建平台,一文说清差别!大家都在用Agent做什么?

代理&#xff08;Agent&#xff09;乃一种智能实体&#xff0c;具备自主环境感知与决策行动能力&#xff0c;旨在达成既定目标。作为个人或组织之数字化替身&#xff0c;AI代理执行特定任务与交易&#xff0c;其核心价值在于简化工作流程&#xff0c;削减繁复性&#xff0c;并有…

MSPM0G3507之电赛小车

一、前言 本文没什么技术分享&#xff0c;纯聊天。以下内容均为笔者的浅薄理解&#xff0c;有不对的地方还请多多包涵。 二、相关配置 主控单元&#xff1a;MSPM0G3507SPTR&#xff08;48角&#xff09; 编译环境&#xff1a;Keil5.33、5.39&#xff08;推荐&#xff09;都可 …

Redisson关键参数含义介绍

一、threads&#xff08;线程池数量&#xff09; 对应executor&#xff08;线程池&#xff09; 默认值: 当前处理核数量 * 2 这个线程池数量被所有RTopic对象监听器&#xff0c;RRemoteService调用者和RExecutorService任务共同共享。 二、nettyThreads &#xff08;Netty线…

数据结构与算法-关于堆的基本排序介绍

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、堆排序…

Springboot使用Redis实现分布式锁

1、使用场景和实现方案&#xff1a; 使用场景&#xff1a;本地锁如Lock和Syncronized只能锁住本地进程&#xff0c;在分布式应用中&#xff0c;需要使用分布式锁来更好实现特定的业务。 实现方案&#xff1a;有多种&#xff0c;比如使用mysql、zookeeper、redis&#xff0c;各…

80端口被system占用 ,system进程是4!!!亲测-----解决

最近需要使用nginx&#xff0c;发现80端口北占用 正常情况下&#xff0c;查看那个进程占用&#xff0c;然后找到对应的程序&#xff0c;关闭对应的就可了。 使用 netstat 命令&#xff1a; 打开命令提示符&#xff08;以管理员身份&#xff09;。输入命令 netstat -ano | fi…

vue3 + element plus使用iconfont 自定义font组件颜色大小可修改

vue3 element plus使用iconfont 自定义font组件&颜色大小可修改这里写自定义目录标题 自定义SvgIcon.vue引入iconfontApp.vue中引入组件更改图标大小 参考网上方案新建SvgIcon.vue&#xff0c;但没说明怎么修改颜色及大小&#xff0c;我在这个博客中简单提供下。 自定义Sv…

通用大模型演进路线

随着人工智能技术的飞速发展&#xff0c;通用大模型&#xff08;GLMs&#xff09;已经成为人工智能领域的重要研 究方向。通用大模型拥有超大规模参数&#xff0c;通过大规模数据进行训练&#xff0c;具备强大的学习和推理 能力。这些模型在自然语言处理、图像识别、代码生成等…

skynet 实操篇

文章目录 概述demo启动文件skynet_start配置文件main.luastart函数thread_workerskynet_context_message_dispatchskynet_mq_popdispatch_message 小结 概述 上一篇写完skynet入门篇&#xff0c;这一篇写点实操性质的。 demo 对于一个开源框架&#xff0c;大部分都有他们自己…

史上最全的Seata教学并且连接springcloudAlibaba进行使用

来都来了点个赞收藏一下在走呗~~&#x1f339;&#x1f339;玫瑰 一、Seata是什么 Seata&#xff08;Simple Extensible Autonomous Transaction Architecture&#xff0c;简单可扩展自治事务框架&#xff09;是一种分布式事务解决方案&#xff0c;旨在解决分布式系统中的事务…

UPLOAD-LABS靶场[超详细通关教程,通关攻略]

---------------------------------------- 靶场环境&#xff1a; 下载链接&#xff1a; https://codeload.github.com/c0ny1/upload-labs/zip/refs/heads/master 使用小皮集成环境来完成这个靶场 将文件放到WWW目录下就可以进行访问 ------------------------------------…

Java从入门到精通(十四) ~ 多线程

晚上好&#xff0c;愿这深深的夜色给你带来安宁&#xff0c;让温馨的夜晚抚平你一天的疲惫&#xff0c;美好的梦想在这个寂静的夜晚悄悄成长。 目录 前言 一、多线程是什么&#xff1f; Java中的多线程 二、使用步骤 1.创建方式 1.1 Thread 线程 1.2 Runnable 任务 1.…

iOS ------ KVO KVC

一&#xff0c; KVO KVO介绍 KVO全称KeyValueObserving,俗称键值监听&#xff0c;是苹果提供的一套时事件通知机制。允许对象监听另一个对象特定属性的改变&#xff0c;并在改变时接受事件。一般继承自NSObject的对象都默认支持KVOKVO和NSNotificationCenter都是iOS观察者模式…

MySQL基础练习题11-换座位

题目&#xff1a;交换每两个连续的学生的座位号。如果学生的数量是奇数&#xff0c;则最后一个学生的id不交换。按 id 升序 返回结果表。 准备数据 分析数据 方法一&#xff1a;利用power函数对id进行交换&#xff0c;得出的答案只有0或1 第一步&#xff1a;用power()函数将…