当我们的数据存储到Redis的时候,我们的键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。RedisTemplate默认使用的是JdkSerializationRedisSerializer,StringRedisTemplate默认使用的是StringRedisSerializer。

Spring Data JPA为我们提供了下面的Serializer:GenericToStringSerializer、Jackson2JsonRedisSerializer、JacksonJsonRedisSerializer、JdkSerializationRedisSerializer、OxmSerializer、StringRedisSerializer。

序列化方式对比:

  • JdkSerializationRedisSerializer: 使用JDK提供的序列化功能。 优点是反序列化时不需要提供类型信息(class),但缺点是需要实现Serializable接口,还有序列化后的结果非常庞大,是JSON格式的5倍左右,这样就会消耗redis服务器的大量内存。
  • Jackson2JsonRedisSerializer: 使用Jackson库将对象序列化为JSON字符串。优点是速度快,序列化后的字符串短小精悍,不需要实现Serializable接口。但缺点也非常致命,那就是此类的构造函数中有一个类型参数,必须提供要序列化对象的类型信息(.class对象)。 通过查看源代码,发现其只在反序列化过程中用到了类型信息。

问题描述

我们在用Spring data redis做redis缓存的时候,指定RedisTemplate的key和value序列化时遇到的问题。

  1. RedisTemplate的key指定成StringRedisSerializer序列化会报类型转换错误,如XXX类不能转换成String。
  2. 使用Jackson2JsonRedisSerializer序列化的时候,如果实体类上没有set方法反序列化会报错。

问题分析

问题1:使用StringRedisSerializer做key的序列化时,StringRedisSerializer的泛型指定的是String,传其他对象就会报类型转换错误,在使用@Cacheable注解是key属性就只能传String进来。把这个序列化方式重写了,将泛型改成Object。源码:

/**
* 必须重写序列化器,否则@Cacheable注解的key会报类型转换错误
*
*/
public class StringRedisSerializer implements RedisSerializer<Object> { private final Charset charset; private final String target = "\""; private final String replacement = ""; public StringRedisSerializer() {
this(Charset.forName("UTF8"));
} public StringRedisSerializer(Charset charset) {
Assert.notNull(charset, "Charset must not be null!");
this.charset = charset;
} @Override
public String deserialize(byte[] bytes) {
return (bytes == null ? null : new String(bytes, charset));
} @Override
public byte[] serialize(Object object) {
String string = JSON.toJSONString(object);
if (string == null) {
return null;
}
string = string.replace(target, replacement);
return string.getBytes(charset);
}
}

问题2:我们放弃用jackjson来做value的序列化,使用FastJson来做。重写一些序列化器,并实现RedisSerializer接口。源码如下:

public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {

    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

    private Class<T> clazz;

    public FastJsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
} @Override
public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
} @Override
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return (T) JSON.parseObject(str, clazz);
} }

新加入一种序列化KryoRedisSerializer。速度很快,源码如下:

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException; import java.io.ByteArrayOutputStream; /**
* @param <T>
* @author yuhao.wang
*/
public class KryoRedisSerializer<T> implements RedisSerializer<T> {
Logger logger = LoggerFactory.getLogger(KryoRedisSerializer.class); public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; private static final ThreadLocal<Kryo> kryos = ThreadLocal.withInitial(Kryo::new); private Class<T> clazz; public KryoRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
} @Override
public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return EMPTY_BYTE_ARRAY;
} Kryo kryo = kryos.get();
kryo.setReferences(false);
kryo.register(clazz); try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
Output output = new Output(baos)) {
kryo.writeClassAndObject(output, t);
output.flush();
return baos.toByteArray();
} catch (Exception e) {
logger.error(e.getMessage(), e);
} return EMPTY_BYTE_ARRAY;
} @Override
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null;
} Kryo kryo = kryos.get();
kryo.setReferences(false);
kryo.register(clazz); try (Input input = new Input(bytes)) {
return (T) kryo.readClassAndObject(input);
} catch (Exception e) {
logger.error(e.getMessage(), e);
} return null;
} }

自定义序列化的使用

@Configuration
public class RedisConfig { /**
* 重写Redis序列化方式,使用Json方式:
* 当我们的数据存储到Redis的时候,我们的键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。RedisTemplate默认使用的是JdkSerializationRedisSerializer,StringRedisTemplate默认使用的是StringRedisSerializer。
* Spring Data JPA为我们提供了下面的Serializer:
* GenericToStringSerializer、Jackson2JsonRedisSerializer、JacksonJsonRedisSerializer、JdkSerializationRedisSerializer、OxmSerializer、StringRedisSerializer。
* 在此我们将自己配置RedisTemplate并定义Serializer。
*
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory); FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
// 全局开启AutoType,不建议使用
// ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
// 建议使用这种方式,小范围指定白名单
ParserConfig.getGlobalInstance().addAccept("com.xiaolyuh."); // 设置值(value)的序列化采用FastJsonRedisSerializer。
redisTemplate.setValueSerializer(fastJsonRedisSerializer);
redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
// 设置键(key)的序列化采用StringRedisSerializer。
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.afterPropertiesSet();
return redisTemplate;
} }

Redis 序列化方式StringRedisSerializer、FastJsonRedisSerializer和KryoRedisSerializer的更多相关文章

  1. SpringBoot修改Redis序列化方式

    前言 由于Springboot默认提供了序列化方式并不是非常理想,对于高要求的情况下,序列化的速度和序列化之后大小有要求的情况下,不能满足,所以可能需要更换序列化的方式. 这里主要记录更换序列化的方式 ...

  2. SpringBoot2.x修改Redis序列化方式

    添加一个配置类即可: /** * @Author FengZeng * @Date 2022-03-22 13:43 * @Description TODO */ @Configuration pub ...

  3. 三种序列化方式存取redis的方法

    常见的的序列化反序列方式的效率: protoBuf(PB) > fastjson > jackson > hessian > xstream > java 数据来自于:h ...

  4. springboot系列十一、redisTemplate和stringRedisTemplate对比、redisTemplate几种序列化方式比较

    一.redisTemplate和stringRedisTemplate对比 RedisTemplate看这个类的名字后缀是Template,如果了解过Spring如何连接关系型数据库的,大概不会难猜出 ...

  5. 自定义redis序列化工具

    redis一个优点就是可以将数据写入到磁盘中. 我们知道写入磁盘的数据实际上都是以字节(0101这样的二进制数据)的形式写入的. 这意味着如果我们要将一个对象写入磁盘,就必须将这个对象序列化. jav ...

  6. redis-springboot-redistemplate更改序列化方式

    redisTemplate 默认的序列化方式为 jdkSerializeable, StringRedisTemplate的默认序列化方式为StringRedisSerializer 可以通过手动配置 ...

  7. Springboot+Redis序列化坑

    今天在测试springboot整合redis的时候遇到下面这个坑,百度来百度去发现提示都是ajax的问题,真的是醉了,错误提示如下所示,不信大家可以直接复制百度一下答案是什么(流泪中....),错误如 ...

  8. SpringBoot项目使用RedisTemplate设置序列化方式

    前端时间新项目使用SpringBoot的RedisTemplate遇到一个问题,先简单描述一下问题:不同项目之间redis共用一个,但是我们新项目读不到老项目存储的缓存.新项目搭建的时候没有跟老项目使 ...

  9. Spring Boot 结合 Redis 序列化配置的一些问题

    前言 最近在学习Spring Boot结合Redis时看了一些网上的教程,发现这些教程要么比较老,要么不知道从哪抄得,运行起来有问题.这里分享一下我最新学到的写法 默认情况下,Spring 为我们提供 ...

随机推荐

  1. [Web 前端] 021 js 初识 Javascript

    1. Javascript 简介 1.1 定位 JS 是运行在浏览器端的脚本语言 1.1.1 关于浏览器 JS 由浏览器解释执行 JS 通常被直接嵌入 HTML 页面 1.1.2 关于脚本语言 JS ...

  2. JGit、SvnKit - 版本提交日志(1)提取

    1.相关开源jar包  1>使用JGIT访问git项目  2>使用SVNkit访问svn Git官方JGit使用教程指导 2.Git历史提交日志导出到文件 在项目根目录执行如下命令,将日志 ...

  3. 最长上升子序列(LIS) Medium2

    JGShining's kingdom consists of 2n(n is no more than 500,000) small cities which are located in two ...

  4. python 如何解决高并发下的库存问题??

    python 提供了2种方法解决该问题的问题:1,悲观锁:2,乐观锁 悲观锁:在查询商品储存的时候加锁 select_for_update()  在发生事务的commit或者是事务的rollback时 ...

  5. python中session的使用

  6. linux:输入/输出、重定向、管道

    输入.输出: 程序的默认输入设备,叫标准输入. stdin    键盘   0 程序的默认输出设备,叫标准输出. stdout    监视器    1 程序的默认错误输出设备,叫标准错误输出.stde ...

  7. python学习笔记(14):可视化分析

    一.Matplotlib 1.用于创建出版质量图表的绘图工具库 2.目的的为Python构建一个Matlab式的绘图接口 3.import matplotlib.pyplot as plt:pyplo ...

  8. vue项目1-pizza点餐系统6-路由精讲之复用router-view

    1.在主组件展示二级路由的组件内容,在App.vue中添加 <br> <div class="container"> <!-- row 行排列 --& ...

  9. 在webpack中配置.vue组件页面的解析

    1. 运行`cnpm i vue -S`将vue安装为运行依赖: 2. 运行`cnpm i vue-loader vue-template-compiler -D`将解析转换vue的包安装为开发依赖: ...

  10. linux MySql 在 Master 主从复制配置

    在 Master 服务器上建立用于 Slave 服务器复制数据的帐户 [root@master ~]# mysql mysql> grant replication slave,replicat ...