Spring整合redis的key时出现\xac\xed\x00\x05t\前缀问题

news/2024/10/22 7:18:55/
@AutowiredRedisTemplate redisTemplate;User user=new User(5,"tomhs","tttt");ValueOperations opsForValue = redisTemplate.opsForValue();//存放key,opsForValue.set("user"+user.getId(),user);//读取数据;System.out.println(opsForValue.get("user" + user.getId()));

背景
项目使用Spring的RedisTemplate进行Redis数据存取操作,实际应用中发现Redis中key和value会出现“无意义”乱码前缀\xac\xed\x00\x05t\x00-(样例\xac\xed\x00\x05t\x00-abcd🔤xxxxxx:passport:associated🔑29708)。

这个乱码前缀是怎么产生的呢?有什么含义?是不是固定的?带着这三个问题,我们一探究竟。

疑问探究
怎么产生的
org.springframework.data.redis.core.RedisTemplate实例化需要序列化和反序列化组件,如果我们不指定,默认使用org.springframework.data.redis.serializer.JdkSerializationRedisSerializer进行序列化,而JdkSerializationRedisSerializer最终使用的是Java原生java.io.ObjectOutputStream.ObjectOutputStream(OutputStream)进行序列化。

这个乱码前缀就是ObjectOutputStream进行序列化时添加的。
有什么含义
\x对应0x

\xac\xed对应是0xaced,是ObjectOutputStream的序列化魔数(见java.io.ObjectStreamConstants.STREAM_MAGIC)。

\x00\x05对应是5,是ObjectOutputStream的序列化版本(见java.io.ObjectStreamConstants.STREAM_VERSION)。

这里引出一个小问题:为什么是\x00\x05而不是\x05?

因为上面2个值write时采用的是short,占2个字节。

样例乱码\x05后面有个t,不是很明显。t是转化后的ASCII码值对应字符,对应16进制是0x74,是ObjectOutputStream分配给String类型标记(见java.io.ObjectStreamConstants.TC_STRING)。

\x00-是有\x00和-组成的,是一起的,表示数据的字节数。-是转化后的ASCII码值对应字符,对应16进制是0x2d(10进制是45,样例abcd🔤xxxxxx:passport:associated🔑29708的字符数就是45,1个字符1个字节,字节数也是45)。

是不是固定的
由上面的描述可知,乱码前缀中\xac\xed\x00\x05是固定的,t在String类型情况是不变的,后面2个位(样例\x00-)是数据的字节数,是随key动态变化的。

衍生疑问
为什么显示不一样
为什么有些16进制\x显示,有些ASCII码值对应字符显示?

结合ASCII码对应的字符表,推测和显示系统能支持的字符集有关。

0x20 到 0x7e,都是比较正常的字符,可以显示出来。
字符长度超长会怎样
2个字节表示数据字节数,即最大0xffff,10进制为65535,key长度超过后会怎么样?

经试验,

key长度65535时,乱码为\xac\xed\x00\x05t\xff\xff

key长度65545时,乱码为\xac\xed\x00\x05|\x00\x00\x00\x00\x00\x01\x00\x09

t上面说过,是转化后的ASCII码值对应字符,对应16进制是0x74,是ObjectOutputStream分配给String类型标记(见java.io.ObjectStreamConstants.TC_STRING)。

|也是转化后的ASCII码值对应字符,对应16进制是0x7c,是ObjectOutputStream分配给长字符串类型标记(见java.io.ObjectStreamConstants.TC_LONGSTRING)。

同时,表示数据字节数的数值位数也变成了8位bigint。

综上,不用担心key长度越界。
使用全局化RedisTemplate配置即可

package com.yh.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** @author by 张晨光* @date 2023/11/13 12:06*/
@Configuration
public class RedisConfig {@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory factory){RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(factory);//使用jackson进行序列化Jackson2JsonRedisSerializer jsonRedisSerializer =new Jackson2JsonRedisSerializer(Object.class);//规定序列化规则ObjectMapper objectMapper = new ObjectMapper();/*** 第一个参数指的是序列化的域,ALL指的是字段、get和set方法、构造方法* 第二个参数指的是序列化哪些访问修饰符,默认是public,ANY指任何访问修饰符*/objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);//指定序列化输入的类型,类必须是非final修饰的类objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jsonRedisSerializer.setObjectMapper(objectMapper);//序列化key valueredisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(jsonRedisSerializer);redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(jsonRedisSerializer);redisTemplate.afterPropertiesSet();return redisTemplate;}
}

应用
通过上面的探究,我们知道了“无意义”乱码前缀的含义。

如果我们新接手一个系统,它是这样使用RedisTemplate的。现在我们需要排查一个和缓存相关的问题,需要看下Redis中某个缓存值是否存在?

我们梳理业务组合出key,通过redis-cli尝试get key是获取不到结果的,此时我们可以根据上面规则自己生成“乱码前缀”,通过get “乱码前缀”+key 就可以判断缓存值是否存在了。
在这里插入图片描述

总结
上面使用RedisTemplate的方式是不好的,实际应用中key序列化可以采用StringRedisSerializer。这也是网上大部分文章建议的。
大部分文章只说了表象原因,没有分析更深入的原因。对从已存在数据中排查问题没有帮助,还是需要自己深究。


http://www.ppmy.cn/news/1217791.html

相关文章

一篇博客读懂栈——Stack

目录 一、栈的概念与结构 1.1栈的概念 1.2栈的结构 二、栈的实现 2.1开始前准备stack.h 2.2栈的初始化STInit 2.3栈的销毁STDestroy 2.4栈顶插入STPush 2.5栈顶删除STPop 2.6取栈顶元素STTop 2.7检查栈是否为空STEmpty 2.8栈的元素个数STSize 一、栈的概念与结…

卫星通信和800MHz双管齐下,中国电信对中国移动发起新挑战

依靠国内某科技企业的宣传&#xff0c;卫星通信大热&#xff0c;中国电信也由此成为受益者&#xff0c;日前中国电信又大举招标25万座800MHz 5G基站&#xff0c;显示出中国电信积极以技术优势挑战中国移动。 一、中国电信急起直追 自从4G时代以来&#xff0c;中国电信就在国内通…

2023 年最新企业微信官方会话机器人开发详细教程(更新中)

目标是开发一个简易机器人&#xff0c;能接收消息并作出回复。 获取企业 ID 企业信息页面链接地址&#xff1a;https://work.weixin.qq.com/wework_admin/frame#profile 自建企业微信机器人 配置机器人应用详情 功能配置 接收消息服务器配置 配置消息服务器配置 配置环境变量…

2023nacos源码解读第3集——nacos-client核心功能之微服务调用和配置管理测试

文章目录 1、测试项目2、项目注意事项3、 测试核心功能3.1 测试服务调用与负载均衡3.2 测试配置监听 4、参考文档 1、测试项目 项目地址 nacos-service-a nacos-service-b 2、项目注意事项 项目初始化可以使用aliyun spring initializer ,以更方便的使用springcloud alibaba…

vscode使用flake8设置单行最长字符限制设置失败的问题

vscode使用flake8设置单行最长字符限制设置失败的问题 问题描述解决方案 问题描述 如图所示&#xff0c;使用flake8单行字数过长&#xff0c;就会有有红色底的波浪线 一般情况下很多教程都会让你在setting.json里面设置 但是我打开我的setting.json&#xff0c;发现我已经进…

Java怎么对复杂的数据类型排序和比大小

目录 一.对复杂的数据类型比大小 Comparable接口 compareTo方法 二.对复杂数据类型排序 三.总结 一.对复杂的数据类型比大小 假如我们现在有个学生类&#xff0c;并且我们实例化出了俩个学生对象&#xff0c;他们各自有各自的名字和年龄属性&#xff0c;我们如何对他们进…

H5游戏源码分享-超级染色体小游戏

H5游戏源码分享-超级染色体小游戏 游戏玩法 不断地扩大发展同颜色的色块 用最少的步数完成游戏 <!DOCTYPE html> <html><head><meta charset"UTF-8"><meta name"viewport"content"widthdevice-width,user-scalableno,init…

Linux 系统概述

Part I: Linux 系统概述 什么是 LinuxLinux 的历史和版本Linux 发行版介绍Linux 的优缺点 Part II: Linux 安装与配置 5. 硬件要求与准备工作 6. 安装 Linux 操作系统 7. Linux 系统初始化设置 8. Linux 系统更新与升级 9. Linux 基础配置 Part III: Linux 命令行 10. Linux…