分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 网页技术

Redis 序列化方式StringRedisSerializer、FastJsonRedisSerializer和KryoRedisSerializer

发布时间:2023-09-06 01:58责任编辑:赖小花关键词:暂无标签

当我们的数据存储到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; ???}}

自定义序列化的使用

@Configurationpublic 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

原文地址:https://www.cnblogs.com/alter888/p/9144427.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved