【问题标题】:EhCache Hibernate 2nd level cache maxBytesLocalHeap slowEhCache Hibernate 二级缓存 maxBytesLocalHeap 慢
【发布时间】:2014-11-10 06:33:19
【问题描述】:

我在 Spring 驱动的应用程序中使用 Hibernate (4.2.15.Final) 和 EhCache (2.6.9) 作为二级缓存设置了一个非常标准的持久层。

一切都按预期进行。但是,将条目放入二级缓存有时需要很长时间。

我已在显式 ehcache.xml 文件中配置了域模型类的缓存(我没有配置默认缓存):

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         name="hibernate"
         updateCheck="false" 
         monitoring="autodetect"
         dynamicConfig="false"
         maxBytesLocalHeap="300M"
         maxBytesLocalDisk="500M">

   <cache
    name="org.mycorp.model.MyEntity" 
    eternal="true"
    overflowToDisk="false"
    diskPersistent="false"
    maxBytesLocalHeap="5M" />

   ...

</ehcache>

我在持久性上下文启动时收到以下 INFO 消息:

DefaultSizeOfEngine | using Agent sizeof engine

以及执行期间的以下警告

ObjectGraphWalker | The configured limit of 1,000 object references was reached while attempting to calculate the size of the object graph. Severe performance degradation could occur if the sizing operation continues. [...]

AFAIK ObjectGraphWalker 必须调整放入缓存中的实体的大小,因为我用maxBytesLocalHeap 配置了单个缓存区域。

我的领域模型相当复杂,我知道我可以使用@IgnoreSizeOf 注释来限制图形的行走,但我不确定如何解决这个问题:

  • 是否必须忽略双向关联的一侧以避免循环?
  • 是否必须明确忽略域模型类的临时成员?
  • 一般来说,在将 EhCache 与 Hibernate 一起使用时使用 maxBytesLocalHeap 是否明智,或者我应该满足于 maxEntriesLocalHeap,因为 Hibernate 无论如何都会为每个实体保留一个单独的缓存区域?

[UPDATE]:我发现,临时成员不会被 Hibernate 缓存(见Hibernate: Is it possible to save a transient field in second level cache?),所以无论如何它们都不应该被 ehcache 处理。正确的?

【问题讨论】:

    标签: java hibernate caching jpa ehcache


    【解决方案1】:

    我知道这是一个老问题。但这可能对某人有用。 我也有同样的警告。我花了很多时间来解决这个问题。 就我而言,EhCache 不会忽略所有休眠代理类。我的实体有一些具有惰性关联的字段,并且在 sizeof 测量期间 EhCache 遍历整个休眠图。

    终于找到this page并解决了。

    【讨论】:

    • 我调查了这篇文章,我们的问题与 Hibernate 完全无关;事实上,ehcache 2.10.3 和可能更早的版本都有一个似乎正确的预配置过滤器 (ehcache-2.10.3.jar!/net/sf/ehcache/pool/builtin-sizeof.filter)。这篇文章很有趣,因为我找到了一种方法,可以通过在net.sf.ehcache.pool 上启用调试日志并设置static { System.setProperty("net.sf.ehcache.sizeof.verboseDebugLogging", "true"); } 来查找添加到缓存中的条目。原来这是一个由 Spring @Cacheable 注释的服务,它返回了一个 ResourceBundle
    • 相关的 SizeOf 配置可以在ehcache.org/generated/2.10.4/html/ehc-all/#page/…找到
    【解决方案2】:

    简答

    原来我遇到的问题是在我的模型中使用 Joda-Time 实例的结果(我使用 Jadira's UserType library 来映射 Joda 类型)。

    Joda 类型保留各种内部引用(包括对年表信息的引用导致巨大的对象图),并且 Ehcache 的 SizeOfEngine 遍历这些引用导致我最初的警告。

    我没有发现如何配置 SizeOfEngine 引擎以排除这些引用的干净方法,但我想更干净的方法是强制 Hibernate 首先只将相关信息放入二级缓存中(一次以我的LocalDateTimes为例)。

    更新

    Jadira 在实现其自定义类型时做出了错误的选择:请参阅 my answer here

    更多详情

    这是我发现的关于我的 OP 的内容(使用 Hibernate 4.2.15.Final、EhCache 2.6.9 和 UserType 3.2.0.GA):

    首先,我对 Hibernate 如何在其二级缓存中存储实体存在误解。在阅读了 Lorimer 关于Truly Understanding the Second-Level and Query Caches 的博客文章后,我觉得很多事情都变得更有意义了:

    • 您不必担心双向关联(或循环图),因为 Hibernate 只会将关联的 ID 放入缓存中。即使它将对整个实体的引用放入缓存中(它不会),EhCache 的 SizeOf 引擎也会跟踪图中已经访问过的对象,并且不会将它们调整大小两次。
    • 同样,您不必担心瞬态字段,因为 Hibernate 不会将它们放入缓存中
    • 理论上,maxBytesLocalHeap 配置应该没有任何问题。当前,当您使用 EhCache 的 SizeOf 引擎未正确测量的自定义用户类型时会出现问题。

    【讨论】: