spring-data-redis使用自定义序列化数据 使用 protobuf
2014-01-23 11:12
871 查看
一、 spring-data-redis序列化策略
spring-data-redis提供了多种serializer策略,这对使用jedis的开发者而言,实在是非常便捷。sdr提供了4种内置的serializer:
JdkSerializationRedisSerializer:使用JDK的序列化手段(serializable接口,ObjectInputStrean,ObjectOutputStream),数据以字节流存储,jdk序列化和反序列化数据
StringRedisSerializer:字符串编码,数据以string存储
JacksonJsonRedisSerializer:json格式存储
OxmSerializer:xml格式存储
其中JdkSerializationRedisSerializer和StringRedisSerializer是最基础的序列化策略,其中“JacksonJsonRedisSerializer”与“OxmSerializer”都是基于stirng存储,因此它们是较为“高级”的序列化(最终还是使用string解析以及构建java对象)。
RedisTemplate中需要声明4种serializer,默认为“JdkSerializationRedisSerializer”:
1) keySerializer :对于普通K-V操作时,key采取的序列化策略
2) valueSerializer:value采取的序列化策略
3) hashKeySerializer: 在hash数据结构中,hash-key的序列化策略
4) hashValueSerializer:hash-value的序列化策略
无论如何,建议key/hashKey采用StringRedisSerializer。 这redis服务端用命令行好查看 配置如下:
二、 自定义序列化处理效果
默认是JdkSerializationRedisSerializer存储,占用空间比较大,如图:
类字段信息,字段信息都在里面,如果对于大数据量存储,内存成本很高。 其他的序列化存储也是类似,只是稍微少点。这里使用google protobuf改造序列化。改造后存储如下:
可以发现存储数据减少一半以上。
三、 自定义序列化处理过程
1、添加自定义序列化处理类
主要是实现RedisSerializer接口,使用protocbuf,就需要使用它的序列化方式,serialize对应protocbuf生成类的toByteArray方法,deserialize对应protocbuf生成类的parseFrom方法,代码如下:
2、使用protobuf序列化处理
应为在deserialize时候需要对应类的类型,所以这里反序列化需要使用对应的解析类来处理。这样导致一个问题,我们没有办法使用单例去配置,因为每个解析类都不一样。所以在配置的时候需要使用多例。scope="prototype",在应用中代码如下:
附proto文件
三、 遇到问题
RedisTemplate类型支持不全
默认:
这样在使用上面的方式的时候,如果遇到hash类型,就没有办法设置setValueSerializer,尝试过用
发现HashOperations类型根本就没有办法获取到RedisTemplate,这样也就没有办法设置setValueSerializer了。再想想是否可以new一个呢,结果发现DefaultHashOperations是包内可见,哎,只有改源码,或者建一个新类
extends DefaultHashOperations了
现在spring-data-redis还不完善,需要改进了。
spring-data-redis提供了多种serializer策略,这对使用jedis的开发者而言,实在是非常便捷。sdr提供了4种内置的serializer:
JdkSerializationRedisSerializer:使用JDK的序列化手段(serializable接口,ObjectInputStrean,ObjectOutputStream),数据以字节流存储,jdk序列化和反序列化数据
StringRedisSerializer:字符串编码,数据以string存储
JacksonJsonRedisSerializer:json格式存储
OxmSerializer:xml格式存储
其中JdkSerializationRedisSerializer和StringRedisSerializer是最基础的序列化策略,其中“JacksonJsonRedisSerializer”与“OxmSerializer”都是基于stirng存储,因此它们是较为“高级”的序列化(最终还是使用string解析以及构建java对象)。
RedisTemplate中需要声明4种serializer,默认为“JdkSerializationRedisSerializer”:
1) keySerializer :对于普通K-V操作时,key采取的序列化策略
2) valueSerializer:value采取的序列化策略
3) hashKeySerializer: 在hash数据结构中,hash-key的序列化策略
4) hashValueSerializer:hash-value的序列化策略
无论如何,建议key/hashKey采用StringRedisSerializer。 这redis服务端用命令行好查看 配置如下:
< ! -- redis template definition -- > < bean id = "redisTemplate" class = "org.springframework.data.redis.core.RedisTemplate" p : connection - factory - ref = "jedisConnFactory" scope = "prototype" > < ! -- 使用string主要是key 在 redis 端用命令好读 不然默认的序列化没办法读 -- > < property name = "keySerializer" > < bean class = "org.springframework.data.redis.serializer.StringRedisSerializer" / > < / property > < property name = "hashKeySerializer" > < bean class = "org.springframework.data.redis.serializer.StringRedisSerializer" / > < / property > < / bean >
二、 自定义序列化处理效果
默认是JdkSerializationRedisSerializer存储,占用空间比较大,如图:
类字段信息,字段信息都在里面,如果对于大数据量存储,内存成本很高。 其他的序列化存储也是类似,只是稍微少点。这里使用google protobuf改造序列化。改造后存储如下:
可以发现存储数据减少一半以上。
三、 自定义序列化处理过程
1、添加自定义序列化处理类
主要是实现RedisSerializer接口,使用protocbuf,就需要使用它的序列化方式,serialize对应protocbuf生成类的toByteArray方法,deserialize对应protocbuf生成类的parseFrom方法,代码如下:
package org.springframework.data.redis.serializer; import java.lang.reflect.Method; import com.google.protobuf.GeneratedMessage; /** * @date 2013年12月27日 上午11:18:23 * @version V1.0 * @param <T> * @Description: protocbuf 序列化数据 减少存储空间 * 存在问题,必须设置对应type,这样的话RedisTemplate单例就没有办法是用了!,是用多例 * <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnFactory" scope="prototype"> */ public class ProtocbufRedisSerializer < T > implements RedisSerializer < T > { private Class < T > type; public ProtocbufRedisSerializer(Class < T > type){ this .type = type; } @Override public byte [] serialize(Object t) throws SerializationException { if (t == null) { return SerializationUtils.EMPTY_ARRAY; } try { GeneratedMessage gm = (GeneratedMessage)t; return gm.toByteArray(); } catch (Exception ex) { throw new SerializationException( "Cannot serialize" , ex); } } @SuppressWarnings( "unchecked" ) @Override public T deserialize( byte [] bytes) throws SerializationException { if (SerializationUtils.isEmpty(bytes)) { return null; } try { Method method = type.getMethod( "parseFrom" , new Class[]{bytes.getClass()}); return (T)method.invoke(type, new Object[]{bytes}); } catch (Exception ex) { throw new SerializationException( "Cannot deserialize" , ex); } } public Class < T > getType() { return type; } public void setType(Class < T > type) { this .type = type; } }
2、使用protobuf序列化处理
应为在deserialize时候需要对应类的类型,所以这里反序列化需要使用对应的解析类来处理。这样导致一个问题,我们没有办法使用单例去配置,因为每个解析类都不一样。所以在配置的时候需要使用多例。scope="prototype",在应用中代码如下:
package com.vrv.im.service.impl.helper; import java.util.ArrayList; import java.util.List; import javax.annotation.Resource; import org.springframework.beans.BeanUtils; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.ProtocbufRedisSerializer; import org.springframework.stereotype.Component; import com.vrv.im.domain.IMCommentSubject; import com.vrv.im.protoc.Comment.CommentSubjectInfo; /** * @date 2013年12月26日 上午11:47:22 * @version V1.0 * @param <K> * @param <V> * @Description: 缓存新鲜事的最近两条评论,所有的新鲜事, * protocbuf序列化压缩数据 * 存在问题,必须设置对应type,这样的话RedisTemplate单例就没有办法是用了!,是用多例 * <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnFactory" scope="prototype"> */ @Component public class CommentInfoCacheHandlerForProtocbuf<K, V> { @Resource private RedisTemplate<String, CommentSubjectInfo> template; private String commentInfoKey="commentInfo"; /** * 获取缓存评论 * @param subjectCommentID * @return */ @SuppressWarnings({ "unchecked", "rawtypes" }) public List<IMCommentSubject> getCommentInfoCache(long subjectCommentID){ List<IMCommentSubject> commentList = new ArrayList<IMCommentSubject>(); template.setValueSerializer(new ProtocbufRedisSerializer(CommentSubjectInfo.class)); List<CommentSubjectInfo> list= template.opsForList().range(commentInfoKey+"_"+subjectCommentID, 0, -1);//获取所有 if(list==null) list= new ArrayList<CommentSubjectInfo>(); for(CommentSubjectInfo info :list){ IMCommentSubject subject = new IMCommentSubject(); BeanUtils.copyProperties(info, subject); commentList.add(subject); } return commentList; } /** * 添加缓存信息 * @param commentInfo */ public void addCommentInfoCache(IMCommentSubject commentInfo){ template.setValueSerializer(new ProtocbufRedisSerializer(CommentSubjectInfo.class)); CommentSubjectInfo inf= CommentSubjectInfo.newBuilder() .setSubjectCommentID(commentInfo.getSubjectCommentID()) .setCommentID(commentInfo.getCommentID()) .setCommentUser(commentInfo.getCommentUser()) .setCreateTime(commentInfo.getCreateTime()) .setComment(commentInfo.getComment()) .setReplyToUser(commentInfo.getReplyToUser()) .build(); template.opsForList().leftPush(commentInfoKey+"_"+commentInfo.getSubjectCommentID(), inf); template.opsForList().trim(commentInfoKey+"_"+commentInfo.getSubjectCommentID(), 0, 1);//保留2条 } /** * 删除评论主体缓存 * @param subjectCommentID */ public void deleteCommentInfoCache(long subjectCommentID){ template.delete(commentInfoKey+"_"+subjectCommentID);//所有都删除 } /** * 删除评论主体的某条评论 * @param subjectCommentID */ public void deleteCommentInfoCache(IMCommentSubject commentInfo){ template.setValueSerializer(new ProtocbufRedisSerializer(CommentSubjectInfo.class)); CommentSubjectInfo inf= CommentSubjectInfo.newBuilder() .setSubjectCommentID(commentInfo.getSubjectCommentID()) .setCommentID(commentInfo.getCommentID()) .setCommentUser(commentInfo.getCommentUser()) .setCreateTime(commentInfo.getCreateTime()) .setComment(commentInfo.getComment()) .setReplyToUser(commentInfo.getReplyToUser()) .build(); template.opsForList().remove(commentInfoKey+"_"+commentInfo.getSubjectCommentID(), 0, inf);//相等的评论信息 } }
附proto文件
package com.vrv.im.protoc; //评论信息类 message CommentSubjectInfo{ required sint64 subjectCommentID = 1; required sint64 commentUser = 2; required sint64 replyToUser = 3; required string comment = 4; required sint64 commentID = 5; required sint64 createTime = 6; }
三、 遇到问题
RedisTemplate类型支持不全
默认:
private ValueOperations<K, V> valueOps ; private ListOperations<K, V> listOps ; private SetOperations <K, V> setOps ; private ZSetOperations<K, V> zSetOps ;
这样在使用上面的方式的时候,如果遇到hash类型,就没有办法设置setValueSerializer,尝试过用
@Resource (name= "redisTemplate" ) private HashOperations<String,String,Long> subjectCommentNumHash
发现HashOperations类型根本就没有办法获取到RedisTemplate,这样也就没有办法设置setValueSerializer了。再想想是否可以new一个呢,结果发现DefaultHashOperations是包内可见,哎,只有改源码,或者建一个新类
extends DefaultHashOperations了
现在spring-data-redis还不完善,需要改进了。
相关文章推荐
- Redis 使用spring-data-redis的序列化问题
- spring-data-redis 整合,以及使用kryo序列化代替jdk原生序列化机制
- spring-data集成redis使用 数据集合池
- spring-data-redis使用jdk序列化时increment的异常
- spring-data-redis 使用过程中需要注意的一点(序列化选择)
- spring-data-redis 使用 protobuf进行序列化和反序列
- 使用 Spring data redis 结合 Spring cache 缓存数据配置
- 使用 data-* 属性来嵌入自定义数据
- yii CListView中使用CArrayDataProvider自定义数组作为数据
- 关于Spring Data redis几种对象序列化的比较
- Springdata-redis在项目中的使用
- 关于jQuery使用serializeArray()序列化表单数据,使用FormData()实现AJAX请求的问题
- spring-boot 结合spring-data-redis使用redis的消息队列
- Spring Boot中使用Spring-data-jpa让数据访问更简单、更优雅
- spring-data-redis 使用
- 使用Spring-Data-Redis存储对象(redisTemplate)
- Spring Redis 序列化方式修改——RedisTemplate存取数据异常,在数据前有一串字符
- Spring-Data-Redis-Repository中以自定义class作为id的实现
- Spring-Data-Redis之RedisTemplate的使用
- 五分钟学会使用spring-data-cassandra快速实现数据的访问