RedisTemplate 中序列化方式辨析

在Spring Data Redis中,RedisTemplate 是操作Redis的核心类,它提供了丰富的API来与Redis进行交互。由于Redis是一个键值存储系统,它存储的是字节序列,因此在使用RedisTemplate时,需要指定键(Key)和值(Value)的序列化方式。不同的序列化方式适用于不同的场景。下面将详细介绍几种序列化方法。

序列化如下对象

User 类

public class User implements Serializable {
    String name;
    String ID;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", ID='" + ID + '\'' +
                '}';
    }

    public User(String name, String ID) {
        this.name = name;
        this.ID = ID;
    }

    public User() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getID() {
        return ID;
    }

    public void setID(String ID) {
        this.ID = ID;
    }
}

JdkSerializationRedisSerializer

JdkSerializationRedisSerializer 是使用JDK自带的序列化机制(ObjectOutputStream 和 ObjectInputStream)来序列化和反序列化POJO对象。这种序列化方式会将对象转换成字节序列,并存储在Redis中。这是RedisTemplate中默认的序列化策略之一(但通常不是推荐用于生产环境的默认策略,因为JDK序列化通常效率较低且生成的字节序列较大)。最大的缺点就是:要求序列化的对象要求继承Serializable类,这是DTO无法容忍的一个要求。

优点:
  • 与其他两个比几乎没有优点,超级不推荐!
缺点:
  • 二进制形式存储,不利于查看!94 bytes
  • 序列化生成的字节序列较大,导致网络传输和存储成本较高。
  • 序列化速度慢。
  • 序列化的字节序列是私有的,不便于跨语言或跨平台共享。
代码示例

    @Test
    public void test4(){
        User user = new User("李白","123456");

//        GenericToStringSerializer<Object> genericToStringSerializer = new GenericToStringSerializer<>(Object.class);
//        redisTemplate.setKeySerializer(genericToStringSerializer);
//        JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
//        redisTemplate.setValueSerializer(jdkSerializationRedisSerializer);

        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setValueSerializer(RedisSerializer.java());

        // 记录开始时间
        Instant start = Instant.now();
        redisTemplate.opsForValue().set("test4",user);
        User user2 = (User)redisTemplate.opsForValue().get("test4");
        logger.info(user2.toString());
        // 记录结束时间
        Instant end = Instant.now();

        // 计算运行时间
        long duration = Duration.between(start, end).toMillis();
        logger.info(duration+"ms");
    }

在这里插入图片描述

StringRedisSerializer

StringRedisSerializer 是最简单的序列化器,它直接将字符串(或任何可以转换为字符串的数据)作为字节序列存储,不需要进行任何特殊的序列化操作。它适用于键或值为字符串的场景。

优点:
  • 效率高,不需要额外的序列化/反序列化开销。
  • 易于理解和维护。
缺点:
  • 只能用于字符串数据。

Jackson2JsonRedisSerializer

Jackson2JsonRedisSerializer 是基于Jackson库实现的JSON序列化器,它可以将Java对象序列化成JSON格式的字符串,并存储在Redis中。Jackson是一个流行的JSON处理库,提供了丰富的API来序列化和反序列化Java对象。

优点:
  • User 对象不需要实现 Serializable接口。
  • 生成的JSON格式易于阅读和调试。
  • 序列化后的数据相对较小,传输和存储效率较高,64 bytes。
  • 支持复杂的Java对象,包括嵌套对象和集合。
@Test
public void test3(){
    User user = new User("李白","123456");

    redisTemplate.setKeySerializer(RedisSerializer.string());
//        Jackson2JsonRedisSerializer<User> userJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(User.class);
//        redisTemplate.setValueSerializer(userJackson2JsonRedisSerializer);
    redisTemplate.setValueSerializer(RedisSerializer.json());

    // 记录开始时间
    Instant start = Instant.now();
    redisTemplate.opsForValue().set("test3",user);
    User user2 = (User)redisTemplate.opsForValue().get("test3");
    logger.info(user2.toString());
    // 记录结束时间
    Instant end = Instant.now();

    // 计算运行时间
    long duration = Duration.between(start, end).toMillis();
    logger.info(duration+"ms");
}

在这里插入图片描述

GenericFastJsonRedisSerializer

GenericFastJsonRedisSerializer 是基于Fastjson库实现的JSON序列化器,与JacksonJsonRedisSerializer类似,但它使用的是Fastjson库。Fastjson是另一个流行的JSON处理库,以其高性能著称。

优点:
  • 序列化性能高。
  • 支持复杂的Java对象。
  • 生成的JSON格式易于阅读和调试。
缺点:
  • 可能存在安全漏洞(历史版本中曾发现过安全问题,使用时需注意版本),据测试,FastJson性能不能完全超过Json库,建议生产中别用!。
    @Test
    public void test5(){
        User user = new User("杜甫","123456");

        redisTemplate.setKeySerializer(RedisSerializer.string());
        GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
        redisTemplate.setValueSerializer(genericFastJsonRedisSerializer);

        // 记录开始时间
        Instant start = Instant.now();
        redisTemplate.opsForValue().set("test5",user);
        User user2 = (User)redisTemplate.opsForValue().get("test5");
        logger.info(user2.toString());
        // 记录结束时间
        Instant end = Instant.now();

        // 计算运行时间
        long duration = Duration.between(start, end).toMillis();
        logger.info(duration+"ms");
    }

在这里插入图片描述

总结

  • 可读性:JacksonJsonRedisSerializerGenericFastJsonRedisSerializer 皆可
  • 内存占用:GenericFastJsonRedisSerializer 59 bytes 小于 JacksonJsonRedisSerializer 60 bytes 小于 JdkSerializationRedisSerializer 94 bytes。
  • 耗时:JacksonJsonRedisSerializerGenericFastJsonRedisSerializer 差不多 且 都比 JdkSerializationRedisSerializer

生产环境中,无脑选择JacksonJsonRedisSerializer即可!

总的来说,在选择序列化器时,应根据具体的应用场景和需求来决定使用哪种序列化方式。对于大多数基于Spring Boot和Spring Data Redis的项目,推荐使用JacksonJsonRedisSerializer 来序列化和反序列化Java对象,因为它们提供了灵活性和高性能。

嵌套对象测试

    public Map<String, List<User>> getNestedObj(){
        ArrayList<User> users = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            users.add(new User(UUID.randomUUID().toString().substring(0,8),UUID.randomUUID().toString()));
        }
        HashMap<String, List<User>> nestedObj = new HashMap<>();
        nestedObj.put("one",users);

        return nestedObj;
    }
JdkSerializationRedisSerializer
    @Test
    public void test11(){

        Map<String, List<User>> nestedObj = getNestedObj();
        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setValueSerializer(RedisSerializer.java());
        // 记录开始时间
        Instant start = Instant.now();
        redisTemplate.opsForValue().set("test11",nestedObj);
        // 记录结束时间
        Instant end = Instant.now();

        // 计算运行时间
        long duration = Duration.between(start, end).toMillis();
        logger.info(duration+"ms");

        // 从redis获取nestedObj并反序列化
        Map<String, List<User>> nestedObj2 = (Map<String, List<User>>)redisTemplate.opsForValue().get("test11");
        logger.info(nestedObj2.toString());

    }

在这里插入图片描述

JacksonJsonRedisSerializer
    @Test
    public void test12(){

        Map<String, List<User>> nestedObj = getNestedObj();
        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setValueSerializer(RedisSerializer.json());
        // 记录开始时间
        Instant start = Instant.now();
        redisTemplate.opsForValue().set("test12",nestedObj);
        // 记录结束时间
        Instant end = Instant.now();

        // 计算运行时间
        long duration = Duration.between(start, end).toMillis();
        logger.info(duration+"ms");

        // 从redis获取nestedObj并反序列化
        Map<String, List<User>> nestedObj2 = (Map<String, List<User>>)redisTemplate.opsForValue().get("test12");
        logger.info(nestedObj2.toString());

    }

在这里插入图片描述

GenericFastJsonRedisSerializer
 @Test
    public void test13(){

        Map<String, List<User>> nestedObj = getNestedObj();
        redisTemplate.setKeySerializer(RedisSerializer.string());
        GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
        redisTemplate.setValueSerializer(genericFastJsonRedisSerializer);

        // 记录开始时间
        Instant start = Instant.now();
        redisTemplate.opsForValue().set("test13",nestedObj);
        // 记录结束时间
        Instant end = Instant.now();

        // 计算运行时间
        long duration = Duration.between(start, end).toMillis();
        logger.info(duration+"ms");

        // 从redis获取nestedObj并反序列化
        Map<String, List<User>> nestedObj2 = (Map<String, List<User>>)redisTemplate.opsForValue().get("test13");
        logger.info(nestedObj2.toString());

    }

在这里插入图片描述

相关推荐

  1. RedisTemplate序列配置

    2024-07-11 01:12:03       40 阅读
  2. 自定义redisTemplate实现自定义序列

    2024-07-11 01:12:03       18 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-11 01:12:03       4 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-11 01:12:03       5 阅读
  3. 在Django里面运行非项目文件

    2024-07-11 01:12:03       4 阅读
  4. Python语言-面向对象

    2024-07-11 01:12:03       4 阅读

热门阅读

  1. C编程题收藏

    2024-07-11 01:12:03       9 阅读
  2. 深入Django(六)

    2024-07-11 01:12:03       9 阅读
  3. django models对应的mysql类型

    2024-07-11 01:12:03       9 阅读
  4. Golang 输入与输出

    2024-07-11 01:12:03       7 阅读
  5. vue extend的作用和使用方法

    2024-07-11 01:12:03       10 阅读
  6. Android View滑动冲突解决方案

    2024-07-11 01:12:03       10 阅读
  7. 【Android】ADB 使用指南

    2024-07-11 01:12:03       9 阅读
  8. PHP语言教程与实战案例详解

    2024-07-11 01:12:03       7 阅读
  9. 【Spring Boot AOP中切入表达式格式介绍】

    2024-07-11 01:12:03       9 阅读
  10. C++多线程条件变量 “从入门到实战”

    2024-07-11 01:12:03       9 阅读