拥有Hibernate二级缓存的CPU优势何时会超过最初的命中率



将对象添加到Hibernate二级对象缓存的CPU优势何时超过初始命中率。

我目前正在使用没有二级缓存的Hibernate。这是针对一个处理音乐文件的应用程序(www.jthink.net/songkong),它使用Hibernate,因此可以扩展更多的数据,即它可以处理100000首歌曲,而内存仅略多于1000首歌曲。一旦歌曲被处理,那么这些歌曲就不感兴趣了(除非用户运行Undo)

据我所知,如果我启用二级缓存(用于我的歌曲类),那么将歌曲初始写入缓存将使用更多的cpu,而如果只是写入数据库,对歌曲对象的额外修改也将需要更多的cpu资源。但随后从Ehcache中检索歌曲将比从数据库中检索歌曲所需的资源更少。

我的歌曲是逐文件夹处理的,并经过多个阶段(在不同的执行器上),当它们在下一个执行器上排队时,我们只将歌曲ID作为参数传递,否则,它将使用大量堆内存来存储歌曲对象本身。因此,当一个特定的任务实际上在Executor上运行时,它所做的第一件事就是检索这些id的歌曲。

因此,没有特定的歌曲ID被检索1000次,但每首歌曲通常被写入1到4次,检索10次。因此,如果我们有一个相当小的缓存(因为我想密切控制堆内存),我希望处理前几个文件夹,将它们的歌曲添加到缓存中,然后当它们完成新文件夹中的歌曲时,它们将在缓存中占据位置。

但我的问题是,这值得吗?

根据经验,10次检索与1-4次写入是否有意义使用二级缓存,或者只有当比率更接近100:1时才有用?

真正的答案是:只对其进行基准测试。

写入堆缓存并没有那么昂贵。所以,是的,即使从缓存中检索一次,也会使它比返回数据库更快。

然后,缓存在HashMap之上主要做两件事。它驱逐并过期。

逐出意味着您为缓存设置了一些最大大小。当达到此值时,缓存将收回"最旧"的条目以添加新条目。最旧的有多种定义。Ehcache对一组条目进行采样,并剔除样本中最长时间未访问的条目。

过期意味着给定的条目在某个时刻会被认为是过时的。例如,在用数据库中的最新条目刷新条目之前,您希望保留一个条目1小时。当您获得一个条目时,Ehcache首先查看该条目是否已过期。如果是,它将返回null并从缓存中删除条目。这意味着过期的条目将保留在缓存中,直到您尝试访问它

在您的情况下,您将希望加载一次条目。然后将其缓存。使用它,最后删除它以节省内存。如果你有最后一步,你知道你不再需要这个条目了,就把它删除吧。

如果你不这样做,你将不得不依靠驱逐。因为驱逐算法会先删除过期的条目(如果可以删除过期的,为什么要删除完全有效的条目?)。

您应该计算一个条目应该在缓存中停留多少时间才能通过所有的Executor。这将是您的到期时间(TTL)。然后将缓存大小或多或少调整为NB_EXECUTORS * NB_STEPS。然后它将是当前使用的歌曲的大小。添加新歌时,缓存将需要收回旧条目。在大多数情况下,此条目将过期,因此不会造成任何伤害。

为了防止驱逐(如果找不到过期的条目,这可能会很昂贵),您可以编写一个获取条目的后台例程。它将触发过期。但同样,在使用基准测试确定它实际上更快之前,不要这样做。

最后,您可能希望直接缓存歌曲,而不是使用Hibernate级别2。因为它将需要较少的操作来获得歌曲。此外,当写入二级缓存中的条目时,Hibernate倾向于从缓存中逐出。请确保将其配置为NOT执行此操作。

关于修改的说明。默认情况下,堆缓存上的Ehcache(并且仅在堆缓存上)是每个引用。因此,如果从缓存中检索一个Song对象,然后对其进行修改,那么缓存中的条目也会被修改,因为它实际上是唯一的实例。

然而,Hibernate二级缓存并不是这样工作的。它们将在缓存中保留某种数据库行。这将被转换为歌曲并返回给您。

当您将Song保存到数据库时,Hibernate会将其从缓存中逐出,正如我上面所说的(但您可能会要求在配置中更新缓存,我不确定)。

这就是为什么我认为应该直接缓存,而不是使用二级缓存。但是,请注意,因为Hibernate加载了一个对象。在将它放入缓存之前,您需要将它从Hibernate中分离出来。然后把它附在新的遗嘱执行人身上。否则,例如,如果你有收藏品,奇怪的事情可能会发生。

现在,假设您希望每次都更新缓存和数据库。你有两种方法。

将缓存放在一边,您将更新数据库,然后更新缓存。

通过Cache,您将更新缓存,缓存将负责(原子)更新DB。由于您需要提供一个CacheLoaderWriter实现,因此需要更多地使用缓存。但它可以确保缓存和数据库始终同步。

最新更新