RedisTemplate 中序列化方式辨析

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

序列化如下对象

User 类

java 复制代码
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
  • 序列化生成的字节序列较大,导致网络传输和存储成本较高。
  • 序列化速度慢。
  • 序列化的字节序列是私有的,不便于跨语言或跨平台共享。
代码示例
java 复制代码
    @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对象,包括嵌套对象和集合。
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库,建议生产中别用!。
java 复制代码
    @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对象,因为它们提供了灵活性和高性能。

嵌套对象测试

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

java 复制代码
    @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

java 复制代码
    @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

java 复制代码
 @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());

    }
相关推荐
Code apprenticeship1 小时前
怎么利用Redis实现延时队列?
数据库·redis·缓存
百度智能云技术站1 小时前
广告投放系统成本降低 70%+,基于 Redis 容量型数据库 PegaDB 的方案设计和业务实践
数据库·redis·oracle
装不满的克莱因瓶1 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
黄名富5 小时前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua
G_whang5 小时前
centos7下docker 容器实现redis主从同步
redis·docker·容器
.生产的驴6 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
我叫啥都行9 小时前
计算机基础复习12.22
java·jvm·redis·后端·mysql
阿乾之铭9 小时前
Redis四种模式在Spring Boot框架下的配置
redis
on the way 12311 小时前
Redisson锁简单使用
redis
科马12 小时前
【Redis】缓存
数据库·redis·spring·缓存