spring-data-redis提供了多种serializer策略,这对使用jedis的开发者而言,实在是非常便捷。sdr提供了4种内置的serializer:
其中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改造序列化。改造后存储如下:
可以发现存储数据减少一半以上。
三、 自定义序列化处理过程
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
* @Description: protocbuf 序列化数据 减少存储空间
* 存在问题,必须设置对应type,这样的话RedisTemplate单例就没有办法是用了!,是用多例
*
*/
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;
}
}
应为在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
* @param
* @Description: 缓存新鲜事的最近两条评论,所有的新鲜事,
* protocbuf序列化压缩数据
* 存在问题,必须设置对应type,这样的话RedisTemplate单例就没有办法是用了!,是用多例
*
*/
@Component
public class CommentInfoCacheHandlerForProtocbuf {
@Resource
private RedisTemplate template;
private String commentInfoKey="commentInfo";
/**
* 获取缓存评论
* @param subjectCommentID
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public List getCommentInfoCache(long subjectCommentID){
List commentList = new ArrayList();
template.setValueSerializer(new ProtocbufRedisSerializer(CommentSubjectInfo.class));
List list= template.opsForList().range(commentInfoKey+"_"+subjectCommentID, 0, -1);//获取所有
if(list==null)
list= new ArrayList();
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);//相等的评论信息
}
}
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; }
private ValueOperations valueOps ;
private ListOperations listOps ;
private SetOperations setOps ;
private ZSetOperations zSetOps ;
@Resource (name= "redisTemplate" )
private HashOperations subjectCommentNumHash