在redis中有hset和hget方法可以直接序列化和反序列化集合,今天我自己试了下序列化,然后通过redis的set和get方法来存入缓存中。
redis的代码如下所示:
//存入redis缓存
public void hset(String key,Object object){
try {
Jedis jedis = jedisPool.getResource();
ByteArrayOutputStream bout;
try {
bout = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bout);
out.writeObject(object);
jedis.setex(key.getBytes(),3600,bout.toByteArray());
} finally {
jedis.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
//从redis缓存中取出数据
public Object hget(String key){
try {
Jedis jedis = jedisPool.getResource();
try {
byte[] b = jedis.get(key.getBytes());
ByteArrayInputStream bin = new ByteArrayInputStream(b);
ObjectInputStream in = new ObjectInputStream(bin);
return in.readObject();
} finally {
jedis.close();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
测试代码如下:
List list = new ArrayList<>();
musicList.setTopic("1");
list.add(musicList);
Date date1 = new Date();
redis.hset("qqq",list);
Date date2 = new Date();
List lists = (List) redis.hget("qqq");
Date date3 = new Date();
System.out.println("序列化并且存入缓存:"+(date2.getTime()-date1.getTime()));
System.out.println("从缓存拿出并且反序列化:"+(date3.getTime()-date2.getTime()));
测试结果的时间为:
redis命令窗口查询:
直接用hset和hget方法输出结果为:
为何二者会差距这么多呢,经过网上的资料和自己的测试,我发现是因为序列化和反序列中,hset和hget使用了ProtostuffIOUtil类,这个类进行序列化和反序列化中,他会创建一个RuntimeSchema,下面我分析测试的步骤:
首先我将前几天的代码略微的进行修改,将Schema的创建放到方法里边,redis代码为:
//设置缓存
public void setRedis(String key,MusicList o){
RuntimeSchema schema = RuntimeSchema.createFrom(MusicList.class);
try {
Jedis jedis = jedisPool.getResource();
try {
byte[] b = ProtostuffIOUtil.toByteArray(o,schema, LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE));
jedis.setex(key.getBytes(),3600,b);
} finally {
jedis.close();
}
} catch (Exception e) {
logger.error(e.getMessage(),e);
}
}
测试代码为:
@Test
public void testRedis(){
musicList.setTopic("1");
Date date1 = new Date();
redisTime.setRedis("1",musicList);
Date date2 = new Date();
redisTime.getRedis("1");
Date date3 = new Date();
System.out.println("toRedis:"+(date2.getTime()-date1.getTime()));
System.out.println("fromRedis:"+(date3.getTime()-date2.getTime()));
}
控制台的输出结果为:
可以看出存入缓存的速度变慢了,因为创建了schema的原因,但是从缓存获取的速度没变,那么我们做另一个测试,就是直接从缓存中读取刚刚那个缓存,这个过程中重新创建一个schema,看下结果如何:
从这里我们可以看出来,如果你先前已经创建了一个类的schema,那么这个schema会被缓存下来,接下来从缓存中使用RuntimeSchema.createFrom(xxx)就不是直接创建这个schema,而是先查看缓存中是否有这个类的schema,如果有就直接拿出来,没有就再创建一个schema,所以我刚刚进行了另一次测试,之前已经向redis中缓存了一个key:1的数据,现在测试代码不向redis插入缓存,而是直接向缓存中取出key:1的数据,我们可以发现,这一次取数据的速度就比之前慢很多了。
下面还有一个测试,我在redis代码中添加了一个map,用于存储schema,代码如下
lic class MusicRedis {
private final JedisPool jedisPool;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private static Map,Schema>> map = new ConcurrentHashMap<>();
public MusicRedis(JedisPoolConfig poolConfig,String ip,int port){
jedisPool = new JedisPool(poolConfig,ip,port,3000);
}
ublic static Schema getSchema(Class cls){
Schema schema = (Schema) map.get(cls);
if (schema==null){
schema = RuntimeSchema.createFrom(cls);
if (schema!=null){
map.put(cls,schema);
}
}
return schema;
}
public void Hset(String key,T t){
try {
Jedis jedis = jedisPool.getResource();
try {
byte[] b = ProtostuffIOUtil.toByteArray(t,getSchema((Class) t.getClass()),LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE));
jedis.setex(key.getBytes(),3600,b);
} finally {
jedis.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public T Hget(String key,T t){
try {
Jedis jedis = jedisPool.getResource();
try {
byte[] b = jedis.get(key.getBytes());
if (b!=null){
Schema schema= (Schema) getSchema(t.getClass());
T message = schema.newMessage();
ProtostuffIOUtil.mergeFrom(b,message,schema);
return message;
}
} finally {
jedis.close();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
测试代码如下:
@Test
public void testRedis(){
List list = new ArrayList<>();
musicList.setTopic("1");
list.add(musicList);
Date date1 = new Date();
redis.Hset("1",list);
Date date2 = new Date();
List lists = redis.Hget("1",new ArrayList());
Date date3 = new Date();
redis.Hset("2",list);
Date date4 = new Date();
List lists2 = redis.Hget("2",new ArrayList());
Date date5 = new Date();
System.out.println("序列化并且存入缓存:"+(date2.getTime()-date1.getTime()));
System.out.println("从缓存拿出并且反序列化:"+(date3.getTime()-date2.getTime()));
System.out.println("创建完schema之后重新存入缓存");
System.out.println("序列化并且存入缓存:"+(date4.getTime()-date3.getTime()));
System.out.println("从缓存拿出并且反序列化:"+(date5.getTime()-date4.getTime()));
}
控制台的输出结果如下:
我们可以看到,如果我将schema缓存起来,那么取出缓存和下次缓存的时间几乎为零,这次我再对之前在类里边写的schema进行两次存入redis的测试,测试结果如下:
我们能看出,这个结果跟我在类外添加一个map进行缓存schema的结果差不多。
综合以上几种情况,我分析直接序列化和反序列的速度卡在了类似于schema创建的机制,在使用redis存取数据的时候,应该注意的是schema的创建位置,因为schema的创建相对于redis存取来说,极其消耗时间。
参考链接:
https://blog.csdn.net/wangfei8348/article/details/60140301