当前位置: 首页 > news >正文

基于缓存提高Java模板文件处理性能:减少磁盘I/O的实践与探索

1、优化背景及动机

背景

线上有一个需求:读取模板文件,并根据不同的业务将数据写入模板文件,生成一个新的文件。模板文件本身是不会变的,所以每次生成文件都要去读取一遍模板文件,会有很多的磁盘IO操作,并且如果模板文件比较大的话,会更加的影响性能。
所以这次针对这个问题,我做了如下优化:
1、将模板文件加载到内存中,后续再生成文件时可直接读取内存中的模板文件对象,而不是去磁盘读文件。
2、要确保我们生成新文件的时候,模板文件不能被篡改,所以需要用深拷贝来获取模板文件的拷贝。
3、为了确保并发场景下,同一个模板文件只会被加载一次,我采用ConcurrentMap来实现。
读取模板文件并加载到内存中,以及深拷贝的代码如下:

@Slf4j
public class WordTemplateCache {private static final ConcurrentMap<String, XWPFTemplate> templateCache = new ConcurrentHashMap<>();public static XWPFTemplate getTemplate(String templatePath) throws IOException {// 检查缓存中是否有该模板// 使用 computeIfAbsent 保证同一个模板只加载一次XWPFTemplate document = templateCache.computeIfAbsent(templatePath, path -> {try {return XWPFTemplate.compile(path);} catch (Exception e) {log.error("读取回证模板文件出错:path={}", templatePath, e);return null;}});if (document == null) {throw new IOException("读取回证模板文件出错,path=" + templatePath);}return deepCopyDocument(document);}// 深拷贝 XWPFTemplatepublic static XWPFTemplate deepCopyDocument(final XWPFTemplate originalTemplate) throws IOException {// 创建字节数组输出流ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();try {// 将原始模板写入字节数组流中originalTemplate.write(byteArrayOutputStream);// 将字节数组转换为输入流ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());// 通过输入流创建新的 XWPFTemplate 实例return XWPFTemplate.compile(byteArrayInputStream);} finally {// 关闭流,避免内存泄漏byteArrayOutputStream.close();}}
}

基于模板文件生成新的文件的代码如下:

public static void generateFile(String outFile, Map<String, Object> data) throws IOException {XWPFTemplate template = WordTemplateCache.getTemplate("template/model.docx").render(data);// 输出到新文件try (FileOutputStream out = new FileOutputStream(outFile)) {template.write(out);} finally {// 关闭模板template.close();}}

1.1 传统磁盘I/O操作的影响

在软件开发和系统设计中,I/O操作一直是影响系统性能的关键因素之一。尤其是在高并发场景下,频繁的磁盘I/O会极大地拖慢系统响应时间,导致系统性能瓶颈的产生。具体来说,每次从磁盘读取文件不仅涉及物理设备的操作,还会牵扯到文件系统、缓存、数据传输等多个环节。这些操作过程中的延迟累积,往往成为系统吞吐量的瓶颈。

1.2 线上问题的触发与分析

在实际的线上环境中,当系统需要频繁生成基于模板的文档时,传统的磁盘I/O操作逐渐暴露出了其性能瓶颈。特别是在用户访问量高峰期,生成文档的请求激增,每次都需要从磁盘读取模板文件,这就导致了大量的磁盘I/O操作堆积,进而使系统响应时间变长,用户体验下降。此外,频繁的磁盘访问也会增加硬件设备的磨损,缩短其使用寿命。

1.3 解决方案的提出

针对上述问题,考虑到模板文件本质上是静态且固定不变的,因此可以通过将这些模板文件缓存到内存中,来减少不必要的磁盘I/O操作。这不仅能够显著提升系统的性能,还可以通过减少磁盘访问次数来降低硬件磨损,延长系统的使用寿命。

2.优化方案的详细设计

2.1 缓存机制的引入

为了有效地缓存模板文件,可以采用基于 ConcurrentHashMap 的缓存机制。ConcurrentHashMap 是Java中用于解决高并发环境下的线程安全问题的集合之一,其高效的读写性能使得其在缓存场景中得到了广泛应用。

在优化方案中,我们通过 ConcurrentHashMap 来存储已加载的模板文件。每次生成新文档时,首先检查缓存中是否存在相应的模板文件,如果存在则直接使用,否则从磁盘读取模板文件并存入缓存。为保证同一模板文件在多线程环境下只被加载一次,采用了 computeIfAbsent 方法,这是一种高效且线程安全的惰性加载方式。

2.2 深拷贝机制的实现

为了确保缓存中的模板文件不被篡改,每次使用模板文件时,需要对其进行深拷贝。深拷贝能够生成一个与原模板文件内容完全相同但独立的副本,从而保证了后续的操作不会影响缓存中的原始模板。

深拷贝的实现主要通过将模板文件的内容写入字节数组,然后再通过字节数组生成一个新的 XWPFTemplate 对象。这样做不仅能够避免数据篡改,还可以有效减少不必要的重复操作,进一步提高系统性能。

2.3 优化流程的简化

在原始流程中,每次生成文档都需要执行三步操作:从磁盘读取模板文件、填充数据、输出生成的新文件。而在优化方案中,简化了第一步操作。通过将模板文件缓存至内存中,只需在第一次加载时进行磁盘读取,后续操作则直接使用缓存中的模板文件,大幅减少了磁盘I/O操作的次数,从而提升了系统整体性能。

3.实际应用与性能提升

3.1 实际应用场景分析

在实际的生产环境中,模板文件的使用频率往往非常高。例如,在电子合同生成、自动化报表生成等场景中,系统需要频繁生成基于模板的文档。在这种场景下,采用缓存优化方案后,能够显著减少磁盘I/O操作,提升系统性能。

3.2 性能测试与对比

为了评估优化方案的实际效果,我们对系统在优化前后的性能进行了对比测试。测试结果表明,在高并发场景下,优化后的系统响应时间明显缩短,吞吐量显著提升。同时,由于磁盘I/O操作的减少,系统的资源占用率也有所下降,进一步证明了该优化方案的有效性。

3.3 用户体验的提升

通过减少文档生成时间,用户在访问系统时的等待时间大幅缩短,系统的响应速度得到了显著提升。用户体验的提升,不仅能够提高用户的满意度,还能够为系统带来更多的流量和用户粘性。

4.优化方案的优缺点分析

4.1 优点分析

  1. 性能提升明显:缓存机制有效减少了不必要的磁盘I/O操作,显著提升了系统的响应速度和吞吐量。

  2. 降低了硬件磨损:减少磁盘访问次数,能够降低磁盘硬件的磨损,从而延长系统的使用寿命。

  3. 代码维护简便:通过引入缓存机制和深拷贝机制,使得代码逻辑更加清晰,维护起来更加方便。

4.2 缺点与潜在问题

  1. 内存占用增加:由于模板文件被缓存至内存中,可能会导致内存占用增加,尤其是在模板文件较多的情况下。

  2. 缓存失效问题:如果模板文件需要更新,缓存中的文件也需要及时同步,否则可能导致生成的文档不符合最新要求。

  3. 深拷贝的性能开销:虽然深拷贝保证了数据的安全性,但其实现方式涉及大量的内存操作,可能会在某些场景下增加系统开销。

5.进一步优化建议

5.1 引入缓存过期机制

为了解决缓存失效的问题,可以考虑为缓存引入过期机制。通过设置过期时间,定期清理缓存中过期的模板文件,确保生成的文档始终使用最新的模板文件。此外,可以通过监听文件系统的变化事件,实时更新缓存中的模板文件。

5.2 使用内存映射文件

对于模板文件较大的场景,可以考虑使用内存映射文件(Memory-Mapped Files)来进一步减少内存占用。内存映射文件可以将文件映射到内存地址空间中,从而在不占用实际内存的情况下,实现对文件的快速访问。

关于内存映射文件的知识,有专门的一篇文章来介绍该知识:内存映射文件(Memory-Mapped Files)在Java中的应用详解

5.3 优化深拷贝机制

为减少深拷贝的性能开销,可以考虑引入更高效的深拷贝实现方式。例如,通过对象序列化的方式实现深拷贝,或采用基于直接内存复制的方案,以降低系统开销。

5.4 分布式缓存的引入

在多节点分布式系统中,可以引入分布式缓存(如Redis)来共享模板文件的缓存,从而在多个节点间共享模板文件,进一步提升系统的整体性能与扩展性。

6.结论

本次优化方案通过引入缓存机制和深拷贝机制,成功地解决了频繁磁盘I/O操作导致的性能瓶颈问题。在实际应用中,该优化方案不仅提升了系统性能,还有效降低了硬件磨损,延长了系统的使用寿命。尽管该方案在内存占用、缓存失效等方面存在一些不足,但通过进一步的优化措施,可以有效解决这些问题,进一步提升系统的整体性能与稳定性。

未来,随着系统的不断发展与应用场景的拓展,该优化方案仍需根据实际需求进行调整与优化,以适应更复杂的业务需求与更高的性能要求。通过不断优化与迭代,系统的性能与用户体验将得到持续提升,为用户提供更加优质的服务体验。

相关文章:

基于缓存提高Java模板文件处理性能:减少磁盘I/O的实践与探索

1、优化背景及动机 背景 线上有一个需求&#xff1a;读取模板文件&#xff0c;并根据不同的业务将数据写入模板文件&#xff0c;生成一个新的文件。模板文件本身是不会变的&#xff0c;所以每次生成文件都要去读取一遍模板文件&#xff0c;会有很多的磁盘IO操作&#xff0c;并…...

C/C++ 线程局部存储(TLS)

在C或C中&#xff0c;线程局部存储&#xff08;Thread-Local Storage&#xff0c;简称TLS&#xff09;是一种用于存储线程特有数据的方法。这意味着每个线程都可以访问它自己的变量实例&#xff0c;而不会影响到其他线程中的同名变量。这在多线程程序中非常有用&#xff0c;因为…...

碰撞检测 | 基于ROS Rviz插件的多边形碰撞检测仿真平台

目录 0 专栏介绍1 基于多边形的碰撞检测2 碰撞检测仿真平台搭建2.1 多边形实例2.2 外部服务接口2.3 Rviz插件化 3 案例演示3.1 功能介绍3.2 绘制多边形 0 专栏介绍 &#x1f525;课设、毕设、创新竞赛必备&#xff01;&#x1f525;本专栏涉及更高阶的运动规划算法轨迹优化实战…...

nginx实验

源码编译 解压 进入目录查看文件 环境监测文件 下载c语言编译工具 指定功能 成功 拷贝 关闭 删除 关闭debug 启动 nginx的平滑升级以及版本回滚 解压 备份 查看进程 回滚 nginx命令的常用参数 查看参数 检测配置文件中的语法 测试并打印 静默模式 设置全局指令&#xff08;不能…...

新技术能够区分真实照片和 AI 伪造图片,但为何平台没有使用?|TodayAI

随着生成式 AI 图像工具的快速发展&#xff0c;网络上越来越多的图像真假难辨。尽管已有技术能够区分真实照片和 AI 伪造图片&#xff0c;但大多数在线平台尚未充分利用这一技术。随着美国总统大选临近&#xff0c;网络上充斥着关于候选人唐纳德特朗普和卡玛拉哈里斯的各种照片…...

测量 Redis 服务器的固有延迟

redis-cli --intrinsic-latency redis-cli --intrinsic-latency 命令用于测量 Redis 服务器的固有延迟。 固有延迟指的是 Redis 服务器处理一个命令所需的最短时间&#xff0c;不包括网络延迟。通过这个测量&#xff0c;我们可以了解 Redis 服务器本身的性能&#xff0c;而不…...

【JVM】JVM内存模型与操作系统内存模型(一)

JVM内存模型与操作系统内存模型 Java进程在操作系统内存中的结构 JVM内存模型 可以这样理解:JVM内存模型其实就是JVM在启动的时候从操作系统内存中要了一块大内存&#xff0c;然后将这个大内存分成五个区域:方法区、堆区、虚拟机栈、本地方法栈、本地方法栈、程序计数器.其实叫…...

构建基于LLM的应用程序——为您的应用程序选择合适的LLM

。 在本章中&#xff0c;将引导您完成为应用程序选择合适LLM的过程。我们将涵盖以下几个主题&#xff1a; 市场上最具前景的LLM概览比较LLM时应使用的主要标准和工具规模与性能之间的权衡 在本章结束时&#xff0c;您应该能够清楚地理解如何为您的应用程序选择合适的LLM&…...

raksmart站群服务器多IP配置要求

RakSmart是一家提供多种服务器解决方案的服务商&#xff0c;其中包括针对站群服务的多IP服务器。这类服务器特别适合那些需要大量独立IP地址的业务&#xff0c;例如站群、多域名托管等。下面我们就来了解一下RakSmart站群服务器的多IP配置要求及相关信息。 什么是站群服务器? …...

【Web IDE】WebContainer容器在浏览器中启动运行nodejs并使用vite启动项目

参考了文章WebContainer/api 基础&#xff08;Web IDE 技术探索 一&#xff09; 在浏览器中运行vite的vue3项目 示例站点 最终效果 主要流程 加载WebContainer》加载代码压缩包>解压代码压缩包》生成文件树》挂载文件树》pnpm安装依赖》启动项目 代码 <script setup…...

Linux 多线程

目录 1 多线程的概念 1.1 再次理解进程的地址空间和页表 1.2 线程 2 线程控制 2.1 创建线程 pthread_create 2.2终止线程 2.3 线程等待 2.4 线程取消 2.5 线程分离 3 原生线程库 4 互斥 &#xff08;锁&#xff09; pthread_mutex_t pthread_mutex_init pthread_mute…...

C语言编写三子棋游戏:从概念到思路到实现

目录 一.文章概述 二.游戏规则概述 三.理解思路 1. 定义游戏数据结构 2. 游戏搭建思路及其步骤 菜单选择列表&#xff1a; 初始化棋盘&#xff1a;所有位置均为空格 创建棋盘样式 设置玩家下棋 设置电脑下棋 检查游戏状态&#xff1a; 四.代码示例 一.game.c部分 …...

React.js如何使用Bootstrap

在 React.js 项目中使用 Bootstrap 有多种方法&#xff0c;主要包括直接引入 Bootstrap CSS 文件和使用 React Bootstrap 库。下面将详细介绍这两种方法。 方法一&#xff1a;直接引入 Bootstrap CSS 文件 这是最简单的方式&#xff0c;只需在项目中引入 Bootstrap 的 CSS 文…...

深入解析:Redis与Nacos分布式锁在业务中的具体应用

时间&#xff1a;2024年08月22日 作者&#xff1a;小蒋聊技术 邮箱&#xff1a;wei_wei10163.com 微信&#xff1a;wei_wei10 音频地址&#xff1a;https://xima.tv/1_HBPYxC?_sonic0 希望大家帮个忙&#xff01;如果大家有工作机会&#xff0c;希望帮小蒋内推一下&#x…...

MySQL索引的性能优化

1.数据库服务器的优化步骤 在数据库调优中&#xff0c;我们的目标就是响应时间更快&#xff0c;吞吐量更大。利用宏观的监控工具和微观的日志分析可以帮我们快速找到调优的思路和方式 数据库服务器的优化步骤 当我们遇到数据库调优问题的时候&#xff0c;该如何思考呢&#xf…...

协方差详解及在日常生活中的应用实例——天气温度与冰淇淋销量的关系

协方差详解及在日常生活中的应用实例——天气温度与冰淇淋销量的关系 文章目录 协方差详解及在日常生活中的应用实例——天气温度与冰淇淋销量的关系引言协方差的概念与背景数学公式推导实例背景数据收集计算过程结果解释计算相关系数为什么使用协方差&#xff1f;结论商业启示…...

Spring Boot3.3.X整合Mybatis-Plus

前提说明&#xff1a; 项目的springboot版本为&#xff1a;<version>3.3.2</version> 需要整合的mybatis-plus版本&#xff1a;<version>3.5.7</version> 废话不多说&#xff0c;开始造吧 1.准备好数据库和表 2.配置全局文件application.properti…...

快速了解软件测试——测试用例的方法

测试用例的编写方法有八种&#xff0c;其中等价类、边界值、判定表、场景法、流程图重要且使用得多 ●等价类●边界值●判定表●因果图[了解]●正交法[了解]●场景法●流程图●错误推测法[了解] 1、等价类 为什么要用等价类划分法? ●从大量数据中划分范围(等价类),然后从每…...

多线程、多进程,还是异步?-- Python 并发 API 如何选择

如何选择正确的 Python 并发 API模块 &#xff1f; Python 标准库提供了三种并发 API &#xff0c; 如何知道你的项目应该使用哪个 API&#xff1f; 在本教程将带逐步了解各API的特性、区别以及各自应用场景&#xff0c;指导你选择最合适的并发 API。 多线程、多进程&#xff0…...

汽车服务管理系统 _od8kr

TOC springboot580汽车服务管理系统 _od8kr--论文 系统概述 该系统由个人管理员和员工管理&#xff0c;用户三部分组成。其中&#xff1a;用户进入系统首页可以实现首页&#xff0c;热销汽车&#xff0c;汽车配件&#xff0c;汽车资讯&#xff0c;后台管理&#xff0c;在线客…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

学习一下用鸿蒙​​DevEco Studio HarmonyOS5实现百度地图

在鸿蒙&#xff08;HarmonyOS5&#xff09;中集成百度地图&#xff0c;可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API&#xff0c;可以构建跨设备的定位、导航和地图展示功能。 ​​1. 鸿蒙环境准备​​ ​​开发工具​​&#xff1a;下载安装 ​​De…...

消防一体化安全管控平台:构建消防“一张图”和APP统一管理

在城市的某个角落&#xff0c;一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延&#xff0c;滚滚浓烟弥漫开来&#xff0c;周围群众的生命财产安全受到严重威胁。就在这千钧一发之际&#xff0c;消防救援队伍迅速行动&#xff0c;而豪越科技消防一体化安全管控平台构建的消防“…...

图解JavaScript原型:原型链及其分析 | JavaScript图解

​​ 忽略该图的细节&#xff08;如内存地址值没有用二进制&#xff09; 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么&#xff1a;保存在堆中一块区域&#xff0c;同时在栈中有一块区域保存其在堆中的地址&#xff08;也就是我们通常说的该变量指向谁&…...

路由基础-路由表

本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中&#xff0c;往往存在多个不同的IP网段&#xff0c;数据在不同的IP网段之间交互是需要借助三层设备的&#xff0c;这些设备具备路由能力&#xff0c;能够实现数据的跨网段转发。 路由是数据通信网络中最基…...