RedisTemplate 中序列化方式辨析

news2024/11/15 12:54:26

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

    }

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1915896.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

伺服【禾川X6】

驱动器&#xff1a; A&#xff1a;脉冲 B&#xff1a;EtherCAT // SV-X6 FB 040 AA 一套360 N&#xff1a;CANopen R&#xff1a;PROFINET 电机&#xff1a; SV-X6 MA 040A-B2 KA 框号&#xff1a; 40 8mm 50…

C++ Primer 总结索引 | 第十六章:模板与泛型编程

1、面向对象编程&#xff08;OOP&#xff09;和泛型编程 都能处理在编写程序时 不知道类型的情况。不同之处在于&#xff1a;OOP 能处理类型 在程序运行之前都未知的情况&#xff1b;而在泛型编程中&#xff0c;在编译时就能获知类型了 2、容器、迭代器 和 算法 都是泛型编程的…

element el-upload 粘贴上传图片

对form中的某一个el-form-item添加 paste.native 事件&#xff0c;事件绑定方法名 handlePaste也可以在其他控件中添加事件监听&#xff0c;这里在当前form-item 这个块使用了&#xff0c;只有当你点击目标区域时才有效。 <el-form-item label"备注图片" paste.n…

skywalking-2-客户端-php的安装与使用

skywalking的客户端支持php&#xff0c;真的很棒。 官方安装文档&#xff1a;https://skywalking.apache.org/docs/skywalking-php/next/en/setup/service-agent/php-agent/readme/ 前置准备 本次使用的php版本是8.2.13: php -v PHP 8.2.13 (cli) (built: Nov 21 2023 09:5…

Prometheus+Grafana主机运行数据

目录 介绍 安装Node Exporter 配置Prometheus 验证配置 导入仪表盘 介绍 Prometheus是一款开源的监控和警报工具&#xff0c;而Node Exporter是Prometheus的一个官方插件&#xff0c;用于采集主机上的各种系统和硬件指标。 安装Node Exporter 下载最新版本的Node Export…

科普文:浮点数精度运算BigDecimal踩坑和填坑

概叙 用过Java的BigDecimal类型&#xff0c;但是很多人都用错了。如果使用不当&#xff0c;可能会造成非常致命的线上问题&#xff0c;因为这涉及到金额等数据的计算精度。 首先说一下&#xff0c;一般对于不需要特别高精度的计算&#xff0c;我们使用double或float类型就可以了…

PHP实现用户认证与权限管理的全面指南

目录 引言 1. 数据库设计 1.1 用户表&#xff08;users&#xff09; 1.2 角色表&#xff08;roles&#xff09; 1.3 权限表&#xff08;permissions&#xff09; 1.4 用户角色关联表&#xff08;user_roles&#xff09; 1.5 角色权限关联表&#xff08;role_permissions…

MySQL之基本查询(下)-表的增删查改

表的增删查改&#xff1a;CRUD : Create(创建), Retrieve(读取)&#xff0c;Update(更新)&#xff0c;Delete&#xff08;删除&#xff09; Update(更新) 语法&#xff1a; UPDATE table_name SET column expr [, column expr ...] [WHERE ...] [ORDER BY ...] [LIMIT ...] …

【观成科技】Websocket协议代理隧道加密流量分析与检测

Websocket协议代理隧道加密流量简介 攻防场景下&#xff0c;Websocket协议常被用于代理隧道的搭建&#xff0c;攻击者企图通过Websocket协议来绕过网络限制&#xff0c;搭建一个低延迟、双向实时数据传输的隧道。当前&#xff0c;主流的支持Websocket通信代理的工具有&#xf…

AnimateLCM:高效生成连贯真实的视频

视频扩散模型因其能够生成连贯且高保真的视频而日益受到关注。然而&#xff0c;迭代去噪过程使得这类模型计算密集且耗时&#xff0c;限制了其应用范围。香港中文大学 MMLab、Avolution AI、上海人工智能实验室和商汤科技公司的研究团队提出了AnimateLCM&#xff0c;这是一种允…

盲盒抽卡机小程序:抽卡机的多样化发展

近几年&#xff0c;盲盒卡牌出现在了大众的生活中&#xff0c;深受学生和年轻消费者的喜爱。卡牌是一种新的盲盒模式&#xff0c;玩家购买后随机获得卡牌&#xff0c;为了收集一整套卡牌&#xff0c;玩家会进行各种复购行为&#xff0c;卡牌逐渐成为了年轻人追捧的休闲方式&…

获取天气数据

获取天气数据其实是一个简单的HTTP接口&#xff0c;根据用户输入的adcode&#xff0c;查询目标区域当前/未来的天气数据&#xff0c;数据来源是中国气象局。 第一步&#xff0c;申请”web服务 API”密钥&#xff08;Key&#xff09;&#xff1b; 链接: 首页 | 高德控制台 (am…

HTTP协议分析/burp/goby/xray

一、HTTP简介 HTTP(超文本传输协议)是今天所有web应用程序使用的通信协议。最初&#xff0c;HTTP只是一个为获取基于文本的静态资源而开发的简单协议&#xff0c;后来人们以名种形式扩展和利用它.使其能够支持如今常见的复杂分布式应用程序。HTTP使用一种用于消息的模型:客户端…

软件产品必须进行确认测试吗?包括哪些测试流程和注意事项?

在当前科技快速发展的时代&#xff0c;软件产品已经成为人们生活和工作中不可或缺的一部分。然而&#xff0c;随着软件产品的增多和复杂性的提升&#xff0c;软件质量的问题也逐渐浮现出来。为了确保软件产品的质量和稳定性&#xff0c;软件产品在开发完成后必须进行确认测试。…

模版初阶(更新)

文章目录 模版介绍函数模版模版匹配规则类模版结言 模版介绍 函数模版分为两个类型&#xff1a; 函数模版类模版 函数模版 语法格式&#xff1a; t e m p l a t e < t y p n a m e T 1 , t y p n a m e T 2... > template<typname T1,typname T2...> template&…

小技巧(更新中)

1.Pycharm使用小技巧pycharm的使用小技巧1---快速找到模块内的函数和类&#xff0c;快速定位查看的模块所在位置_pycharm怎么查找某个函数-CSDN博客 2. Python库之requirments Python库安装之requirements.txt, environment.yml_python requirements-CSDN博客 3.执行.sh脚本的…

N6 word2vec文本分类

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊# 前言 前言 上周学习了训练word2vec模型&#xff0c;这周进行相关实战 1. 导入所需库和设备配置 import torch import torch.nn as nn import torchvision …

10x Visium HD数据分析

–https://satijalab.org/seurat/articles/visiumhd_analysis_vignette 留意更多内容&#xff0c;欢迎关注微信公众号&#xff1a;组学之心 1.数据准备-Seurat Visium HD 数据是由特定空间排列分布的寡核苷酸序列在 2um x 2um 的网格&#xff08;bin&#xff09;中生成的。然…

15. Revit API: Transaction(事务)与 Failures(故障处理)

前言 UI讲完&#xff0c;回到DB这块儿。在Document那篇&#xff0c;提到增删改查操作都是在Document上&#xff0c;是对Documet进行操作。 看到“增删改查”这四个&#xff0c;想到什么了没有&#xff1f; 数据库&#xff08;DB&#xff09;嘛~话说那本经典的红皮数据库的书叫…

Python学习笔记34:进阶篇(二十三)pygame的使用之颜色与字体

前言 基础模块的知识通过这么长时间的学习已经有所了解&#xff0c;更加深入的话需要通过完成各种项目&#xff0c;在这个过程中逐渐学习&#xff0c;成长。 我们的下一步目标是完成python crash course中的外星人入侵项目&#xff0c;这是一个2D游戏项目。在这之前&#xff…