redis序列化

发布于:2025-06-16 ⋅ 阅读:(23) ⋅ 点赞:(0)

        测试环境的接口没有返回值,经过查询发现是直接从redis中读取的数据,由于不清楚是通过什么方式写入的redis(没仔细想办法找到写入的代码),所以查询了开发环境的redis key,发现有数据,就想直接将开发环境的key 和value手动写入到测试环境。手动复制的过程中发现value的中文都是些编码过的16进制的符号,然后在复制到测试环境后,测试环境接口报错,解析对象异常。估计是序列化引起的问题,虽然value只能看到值,但是我记得好像有些会存@class之类的,暂时不清楚,只有回头查找程序是在哪里写入redis的,直接对该key idea中全局查找,就找到了写入的接口,接口逻辑,查询数据库,然后写入redis,手动在数据库添加测试数据,然后调用该接口,成功写入redis。

总结:
  • 1.遇到问题,不能总想着偷懒的方式解决,多思考一步,或许很容易就能找到更合适的方法

  • 2.先将直观的方式和可行的方案都罗列出来,评估选定一个合适的方案。多思考,思考清楚了在动手,而不是动手后发现不行,再思考一下,发现有更简单的方式。

为什么使用redis-cli查询value,中文是16进制数据

原因解析
1.Redis存储的本质

Redis存储的是二进制安全的字节序列(byte[]),不关心内容编码(UTF-8,GBC等)

  • 程序写入时,序列化器(如StringRedisSerializer)会将字符串转为UTF-8编码的字节数组存入redis.

  • 中文字符在UTF-8中通常占3个字节(如“中”->\xE4\xB8\xAD)

2.redis-cli的默认行为
  • 默认以ASCLL安全模式显示数据:非ASCII字符(如中文)会被转义为\xXX形式(每个字节的十六进制值)

  • 这是为了保护终端显示安全,避免二进制数据(如图片)直接输出到终端导致乱码或控制符误触发。

3.应用程序的行为
  • 程序读取数据后,会用相同的序列化器(如UTF-8解码)将字节数组还原为字符串,因此中文正常显示。

若已进入redis-cli交互界面,则在查询命令前加 RAW

#在get命令前加 RAW
​
127.0.0.1:6379> SET your_key "你好"  # 写入测试数据
127.0.0.1:6379> GET your_key         # 默认显示转义字符:"\xe4\xbd\xa0\xe5\xa5\xbd"
127.0.0.1:6379> RAW GET your_key     # 使用 RAW 模式输出
你好

为什么手动复制redis的value到测试环境,会无法解析

根本原因:手动复制破坏了序列化结构
1.序列化器的隐式元信息丢失

当程序使用带类型签名的序列化器(如SpringBoot默认的JdkSerializationRedisSerializerGenericJackson2JsonRedisSerializer)时,数据中会保护额外的类型标识:

// GenericJackson2JsonRedisSerializer 写入的数据实际结构
{
  "@class": "com.example.User",  // 类型签名(Java特有)
  "name": "张三",
  "age": 30
}
  • 手动复制行为

通过redis-cli复制的是人类可读的字符串表示,而非原始二进制数据->类型签名@class 等元信息被破坏或丢失

序列化的必要性:

  1. 数据格式统一性 Redis底层存储基于字节流,复杂对象(如Java/Python对象)无法直接存储。序列化将对象转为二进制或字符串(如JSON),确保数据可被Redis处理。

  2. 跨语言兼容性 微服务架构中,不同语言服务(如Java写入、Python读取)需通用格式(如JSON/MessagePack)实现数据交换

  3. 存储效率与安全 序列化可压缩数据体积(如MessagePack比JSON小30%),同时避免明文存储敏感信息,并通过类型校验防止注入攻击

二、常见序列化方式及对比

序列化器 适用场景 优点 缺点 示例数据类型
StringRedisSerializer 简单字符串、数值 性能最优,内存占用低,直接存储字符串 仅支持基础类型,不支持对象 用户Token、计数器
GenericJackson2JsonRedisSerializer 复杂对象(嵌套结构、集合) 可读性好(JSON格式),跨语言,支持复杂类型 性能较低,内存占用较高,需完整get/set方法 用户信息、订单数据
JdkSerializationRedisSerializer Java对象 支持任意Java对象(需实现Serializable) 二进制不可读,体积大(JSON的5倍),仅限Java 特定Java对象
自定义序列化 特殊需求(如Protobuf) 灵活性高,可优化性能/体积
关键对比:
  • 性能 StringRedisSerializer > GenericJackson2Json > JDK序列化

  • 存储体积:JDK二进制 > JSON > MessagePack/Protobuf

  • 调试友好性:JSON > 字符串 > 二进制

选型建议与最佳实践

1.高性能场景(如缓存热点数据)
  • 优先使用 StringRedisSerializer(Key/Value均适用)

  • 复杂对象手动转为JSON存储,避免全局使用JSON序列化器

// Java示例:手动序列化对象
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(user);
redisTemplate.opsForValue().set("user:1001", json);  // 使用String序列化器
2.跨语言或复杂对象场景
  • 使用 GenericJackson2JsonRedisSerializer,但需确保对象有无参构造器和完整get/set方法

  • 避免Jackson2JsonRedisSerializer(需指定Class类型,灵活性差)

3.规避JDK默认序列化
  • 默认的JdkSerializationRedisSerializer因性能差、体积大、语言绑定强,不推荐生产使用

4.进阶优化
  • 压缩大对象:对超过1KB的数据启用GZIP压缩

  • 版本控制:Key中嵌入版本号(如user:v2:1001),应对字段变更

  • 空值缓存:防止缓存穿透


网站公告

今日签到

点亮在社区的每一天
去签到