本文探讨了各种Java缓存技术的提供,这些技术在改善应用程序性能方面发挥着至关重要的作用,并涵盖了各种场景。
几十年来,信息技术一直在极大地改善业务流程,并成为全球企业的战略解决方案。曾经被视为“可有可无”的东西现在已经成为必备之物。应用程序是任何业务的核心,在近年来,它们的使用量大幅增加。因此,响应时间变得更加重要。数据检索时间在用户体验中起着关键作用,并且在几乎所有商业应用程序中都是一个关键要求。如今有许多影响响应时间的因素,包括网络管道、协议、硬件、软件和互联网速度。庞大的IT基础设施和对系统性能的不断追求严重破坏了任何组织的战略目标。
本文旨在介绍一种Java缓存机制,以改善应用程序的性能。
缓存概念
缓存是用于临时存储频繁访问的数据的内存缓冲区。它通过避免从原始来源再次检索数据来提高性能。实际上,缓存是计算机/网络行业的一个已经应用了相当长时间的概念,因此根据使用情况有不同的缓存实现方式。事实上,路由器、交换机和个人电脑等设备都使用缓存来加速内存访问。另一个非常常见的缓存是在几乎所有个人电脑上使用的Web浏览器缓存,用于存储请求的对象,以避免多次检索相同的数据。在分布式JEE应用程序中,客户端/服务器端缓存在改善应用程序性能方面起着重要作用。客户端缓存用于临时存储通过网络从服务器传输的静态数据,以避免不必要地向服务器发出调用。另一方面,服务器端缓存用于存储从其他资源获取的数据。
缓存可以设计为单个/多个JVM或集群环境。以下是可使用缓存满足非功能性要求的不同可扩展性场景。
纵向扩展
可以通过升级单台机器的资源(CPU、RAM、HDD和SSD)并实施缓存来实现。但是,在将缓存升级到一定限制上存在一些局限性。在下面的用例中,可以通过增加内存并在应用程序级别实施缓存来提高应用程序的性能。
横向扩展
可以通过添加更多的机器并在每台机器上实施应用程序级别的缓存来实现。但是,在与下游应用程序通信方面仍然存在一些限制,无法添加额外的服务器。在下面的用例中,可以通过为每个应用程序添加一个服务器/缓存来提高整体应用程序的性能。数据库在满足此要求方面存在一些限制,但可以通过将静态/主数据存储在缓存中来减轻这种限制。
进程内缓存
进程内缓存使对象能够存储在与应用程序相同的实例中,即缓存在本地对应用程序可用,并共享相同的内存空间。
以下是考虑进程内缓存时的一些重要事项:
如果应用程序仅部署在一个节点上,即只有一个实例,则进程内缓存是正确的选择,可以存储频繁访问的数据并实现快速数据访问。
如果进程内缓存将在应用程序的多个实例中部署,则在所有实例之间保持数据同步可能是一个挑战,并可能导致数据不一致。
如果服务器配置有限,则这种类型的缓存可能会降低任何应用程序的性能,因为它共享相同的内存和CPU。垃圾收集器经常被调用来清理可能导致性能开销的对象。如果数据驱逐没有有效管理,可能会发生内存溢出错误。
内存分布式缓存
分布式缓存(键/值对象)可以在应用程序外部构建,支持对数据存储库的读写操作,在RAM中保存频繁访问的数据,并避免不断从数据源获取数据。这样的缓存可以部署在由多个节点组成的集群上,形成一个单一的逻辑视图。缓存客户端使用哈希算法来确定集群节点中对象的位置。
以下是考虑分布式缓存时的一些重要事项:
对于在关键性能环境下具有多个实例的集群中的中大型应用程序,内存分布式缓存是最佳选择。数据不一致性和共享内存不再是问题,因为分布式缓存以单一逻辑状态的形式部署在集群中。
由于需要通过网络访问缓存,跨进程的开销包括延迟、故障以及对象序列化等,可能会降低性能。
与进程内缓存相比,其实现更加困难。
内存数据库
这种类型的数据库也被称为主存数据库。它将数据存储在RAM中,而不是硬盘上,以实现更快的响应。数据以压缩格式存储,并具有良好的SQL支持。可以使用相关的数据库驱动程序来替代现有的关系数据库管理系统(RDBMS)。
通过用内存数据库替换RDBMS,可以提高应用程序的性能,而无需更改应用程序层。只能进行纵向扩展以扩大内存数据库的规模。
内存数据网格
这种分布式缓存解决方案可以快速访问频繁使用的数据。数据可以在多个节点上进行缓存、复制和分区。
实施内存数据网格将提高应用程序的性能并扩展其规模,而无需更改关系数据库管理系统(RDBMS)。
关键特点包括:
在内存中对数据进行并行计算
在内存中进行搜索、聚合和排序
在内存中进行事务管理
事件处理
缓存的使用场景
有一些使用场景可以通过各种商业/开源缓存框架来提高应用程序性能,并且可以在任何企业应用程序中进行配置。以下是常见的缓存使用场景。
应用程序缓存
应用程序缓存是应用程序使用的本地缓存,用于将频繁访问的数据保留在内存中。应用程序缓存会自动驱逐条目以维护其内存占用。
Level 1 (L1) Cache
这是每个会话的默认事务性缓存。它可以由任何Java持久化框架(JPA)或对象关系映射(ORM)工具进行管理。
L1缓存存储属于特定会话的实体对象,并在会话关闭后被清除。如果在一个会话中有多个事务,所有实体将来自这些事务。
Level 2 (L2) Cache
L2缓存可以配置为提供自定义缓存,用于保存所有需要缓存的实体数据,包括属性、关联和集合。它在会话工厂级别进行配置,并在会话工厂可用时存在。
L2缓存可以配置为在以下范围内使用:
应用程序中的会话。
具有相同数据库的相同服务器上的应用程序。
同一数据库上不同服务器上的不同应用程序的集群。
如果配置了L2缓存,则可以使用L1/L2缓存的方式:
标准的ORM框架首先在L1缓存中查找实体,然后再在L2缓存中查找。L1缓存是查找实体的初始搜索空间。如果找到实体的缓存副本,则返回该实体。
如果在L1缓存中找不到缓存的实体,则会在L2缓存中进行查找。
如果在L2缓存中找到缓存的实体,则将其存储在L1缓存中,然后返回。
如果实体既不在L1缓存中也不在L2缓存中,则从数据库中获取该实体,并在返回给调用方之前将其存储在两个缓存中。
当任何会话对实体进行修改时,L2缓存会进行验证/刷新。
如果数据库被外部进程修改,即没有应用程序会话参与,那么除非通过框架API或自定义API实现某种缓存刷新策略,否则无法隐式刷新L2缓存。
以下通信图说明了使用L1/L2缓存的情况:
混合缓存
混合缓存是标准ORM框架提供的缓存与开源/自定义/JDBC API实现的缓存的结合。应用程序可以使用混合缓存来利用仅限于标准ORM框架的缓存功能。这种类型的缓存通常在响应时间至关重要的关键任务应用程序中使用。
缓存设计考虑因素
缓存设计时需要考虑数据加载/更新、性能/内存大小、逐出策略、并发性和缓存统计等因素。
数据加载/更新
将数据加载到缓存中是一个重要的设计决策,以保持所有缓存内容的一致性。可以考虑以下方法来加载数据:
使用标准ORM框架(如Hibernate或OpenJPA)提供的默认函数/配置。
使用开源缓存API(如Google Guava)或COTS产品(如Coherence、Ehcache或Hazelcast)实现键值映射。
通过自动或显式插入编程方式加载实体。
通过同步或异步通信进行外部应用程序加载。
性能/内存大小 32/64位
可用内存是实现性能SLA的重要因素,它取决于32/64位JRE,而后者又依赖于32/64位CPU架构的机器。在32位系统/JRE中,约有1.5 GB的堆留给应用程序使用,而在64位系统/JRE中,堆大小取决于RAM大小。
内存的高可用性会增加运行时的成本,并可能产生负面影响。
与32位相比,64位需要30-50%更多的堆空间,因为内存布局不同。
维护更大的堆空间需要更多的GC工作来清理未使用的对象,可能会降低性能。可以通过调整GC进行微调以限制GC暂停时间。
逐出策略
逐出策略使缓存能够确保缓存的大小不超过最大限制。根据逐出策略,会从缓存中删除现有元素,但可以根据应用程序要求进行定制。常用的缓存逐出算法有:
最近最少使用(LRU)
最不经常使用(LFU)
先进先出(FIFO)
并发性
并发性是企业应用程序中常见的问题。它会导致冲突并使系统处于不一致状态。当多个客户端在缓存刷新期间同时尝试更新相同的数据对象时,就会发生并发性问题。常见的解决方案是使用锁定机制,但这可能会影响性能。因此,应考虑优化技术。
缓存统计
缓存统计信息有助于了解缓存的健康状况,并提供有关缓存行为和性能的信息。通常,可以使用以下属性来进行缓存统计:
命中计数:找到对象时遇到的查找次数
未命中计数:未找到对象时遇到的查找次数
成功加载计数:成功加载条目的数量
总加载时间:加载元素所用的总时间
加载异常计数:加载条目时抛出的异常数量
逐出计数:从缓存中逐出的条目数
概述:各种缓存解决方案
Java有多种可用的缓存解决方案 - 找到适合您的用例的正确选择取决于您的需求。以下是一些问题和比较,可帮助确定最具成本效益且可行的缓存解决方案:
您需要一个轻量级还是完整的缓存解决方案?
您需要一个开源、商业还是框架提供的缓存解决方案?
您需要进程内还是分布式缓存?
一致性和延迟需求之间的权衡如何?
您是否需要维护事务性/主数据的缓存?
您是否需要复制缓存?
关于性能、可靠性、可扩展性和可用性方面有什么要考虑的?
根据这些问题和比较,您可以确定最适合您需求的成本效益和可行性的缓存解决方案。
缓存解决方案 | 缓存类型 | 开源 | 复制 | (JSR-107)兼容 | 配置复杂性 | 逐出算法 | 集群支持 |
---|---|---|---|---|---|---|---|
Ehcache | Level 2级别 | 是 | 是 | 是 | 简单 | LRU, LFU | 是 |
Hazelcast | Level 2级别 | 是 | 是 | 是 | 简单 | LRU, LFU | 是 |
Redis/Memcached | 分布式 | 是 | 是 | 是 | 中等 | LRU | 是 |
Google Guava | 进程内 | 是 | 否 | - | 简单 | LRU | 否 |
Coherence | 分布式 | 否 | 是 | 是 | 复杂 | LRU, LFU, 混合, 自定义 | 是 |
请注意,复制缓存意味着缓存可以在集群中多个节点之间维护相同的数据副本。这有助于实现高可用性和容错性。
(JSR-107)兼容表示符合Java Caching API标准,该标准提供了一组用于在Java中进行缓存的公共接口和功能。
配置复杂性指的是配置和设置缓存解决方案的难易程度。
逐出算法确定在缓存达到最大大小时从中删除哪些元素。LRU表示最近最少使用,FLU表示最不经常使用,LFU表示最频繁使用率最低。
集群支持表示缓存解决方案是否支持集群,并能够将数据分布在集群中的多个节点之间。
根据这些因素,您可以选择最适合您特定需求的缓存解决方案。文章来源:https://www.toymoban.com/diary/system/636.html
关键词:Java缓存技术,改善应用程序性能,性能优化,内存缓存文章来源地址https://www.toymoban.com/diary/system/636.html
到此这篇关于如何优化应用程序性能的内存缓存解决方案-Java缓存技术的文章就介绍到这了,更多相关内容可以在右上角搜索或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!