Redis 序列化方式StringRedisSerializer、FastJsonRedisSerializer和KryoRedisSerializer
当我们的数据存储到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序列化时遇到的问题。
- RedisTemplate的key指定成StringRedisSerializer序列化会报类型转换错误,如XXX类不能转换成String。
- 使用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的更多相关文章
- SpringBoot修改Redis序列化方式
前言 由于Springboot默认提供了序列化方式并不是非常理想,对于高要求的情况下,序列化的速度和序列化之后大小有要求的情况下,不能满足,所以可能需要更换序列化的方式. 这里主要记录更换序列化的方式 ...
- SpringBoot2.x修改Redis序列化方式
添加一个配置类即可: /** * @Author FengZeng * @Date 2022-03-22 13:43 * @Description TODO */ @Configuration pub ...
- 三种序列化方式存取redis的方法
常见的的序列化反序列方式的效率: protoBuf(PB) > fastjson > jackson > hessian > xstream > java 数据来自于:h ...
- springboot系列十一、redisTemplate和stringRedisTemplate对比、redisTemplate几种序列化方式比较
一.redisTemplate和stringRedisTemplate对比 RedisTemplate看这个类的名字后缀是Template,如果了解过Spring如何连接关系型数据库的,大概不会难猜出 ...
- 自定义redis序列化工具
redis一个优点就是可以将数据写入到磁盘中. 我们知道写入磁盘的数据实际上都是以字节(0101这样的二进制数据)的形式写入的. 这意味着如果我们要将一个对象写入磁盘,就必须将这个对象序列化. jav ...
- redis-springboot-redistemplate更改序列化方式
redisTemplate 默认的序列化方式为 jdkSerializeable, StringRedisTemplate的默认序列化方式为StringRedisSerializer 可以通过手动配置 ...
- Springboot+Redis序列化坑
今天在测试springboot整合redis的时候遇到下面这个坑,百度来百度去发现提示都是ajax的问题,真的是醉了,错误提示如下所示,不信大家可以直接复制百度一下答案是什么(流泪中....),错误如 ...
- SpringBoot项目使用RedisTemplate设置序列化方式
前端时间新项目使用SpringBoot的RedisTemplate遇到一个问题,先简单描述一下问题:不同项目之间redis共用一个,但是我们新项目读不到老项目存储的缓存.新项目搭建的时候没有跟老项目使 ...
- Spring Boot 结合 Redis 序列化配置的一些问题
前言 最近在学习Spring Boot结合Redis时看了一些网上的教程,发现这些教程要么比较老,要么不知道从哪抄得,运行起来有问题.这里分享一下我最新学到的写法 默认情况下,Spring 为我们提供 ...
随机推荐
- vue项目中,无需打包而动态更改背景图以及标题
1.背景 今天,项目经理对已完成的项目提出了一个需求,即项目的基础功能目前针对于各个基层法院都适用,而对于不同的法院,我们每次都需要前端研发来更改所属法院的法院名称以及背景图,这样对于演示者来说是非常 ...
- mysql驱动表与被驱动表及join优化
驱动表与被驱动表 先了解在join连接时哪个表是驱动表,哪个表是被驱动表:1.当使用left join时,左表是驱动表,右表是被驱动表2.当使用right join时,右表时驱动表,左表是驱动表3.当 ...
- [知乎]自己真是Know Nothing 历史上有哪些细思恐极的事件?
作者:敖让链接:https://www.zhihu.com/question/38374479/answer/652432284来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...
- [转帖]怎样选择(FC-SAN)光纤通道(存储)交换机
怎样选择(FC-SAN)光纤通道(存储)交换机 https://blog.csdn.net/sinat_30171789/article/details/50510936 交换机的种类非常多... ...
- springboot swagger教程😀
传送门开启:https://www.ibm.com/developerworks/cn/java/j-using-swagger-in-a-spring-boot-project/index.html
- kafka 安装教程
安装详述: https://www.jianshu.com/p/596f107e901a 3.0:运行:cd 到: D:\Installed_software\Professional\kafka_2 ...
- [BZOJ3451]Normal(点分治+FFT)
[BZOJ3451]Normal(点分治+FFT) 题面 给你一棵 n个点的树,对这棵树进行随机点分治,每次随机一个点作为分治中心.定义消耗时间为每层分治的子树大小之和,求消耗时间的期望. 分析 根据 ...
- 【输入法】向Android端Gboard字典中导入PC端搜狗细胞词库
[输入法]向Android端Gboard字典中导入PC端搜狗细胞词库 环境 Android 5.1.1 Gboard 8.7.10.272217667-release -armeabi-v7a PC端 ...
- jQuery之操作样式的css方法
注意点都在代码里 <style> div { width: 200px; height: 200px; background-color: pink; } </style> & ...
- 自己写的SqlHelper,提示在调用"Fill"前,SelectCommand 属性尚未初始化.错误
namespace 操作数据{ class SqlHelper { public DataSet SqlTODs(string cmdstring) { ...