关于序列化和反序列化

在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()));

测试结果的时间为:


image.png

redis命令窗口查询:


关于序列化和反序列化_第1张图片
image.png

直接用hset和hget方法输出结果为:


image.png

为何二者会差距这么多呢,经过网上的资料和自己的测试,我发现是因为序列化和反序列中,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()));
      }

控制台的输出结果为:


image.png

可以看出存入缓存的速度变慢了,因为创建了schema的原因,但是从缓存获取的速度没变,那么我们做另一个测试,就是直接从缓存中读取刚刚那个缓存,这个过程中重新创建一个schema,看下结果如何:


image.png

从这里我们可以看出来,如果你先前已经创建了一个类的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()));
       }

控制台的输出结果如下:


关于序列化和反序列化_第2张图片
image.png

我们可以看到,如果我将schema缓存起来,那么取出缓存和下次缓存的时间几乎为零,这次我再对之前在类里边写的schema进行两次存入redis的测试,测试结果如下:


image.png

我们能看出,这个结果跟我在类外添加一个map进行缓存schema的结果差不多。

综合以上几种情况,我分析直接序列化和反序列的速度卡在了类似于schema创建的机制,在使用redis存取数据的时候,应该注意的是schema的创建位置,因为schema的创建相对于redis存取来说,极其消耗时间。

参考链接:
https://blog.csdn.net/wangfei8348/article/details/60140301

你可能感兴趣的:(关于序列化和反序列化)