具有Hibernate二级缓存的CPU优势何时超过初始命中

Pau*_*lor 5 java hibernate ehcache

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

我目前正在使用没有二级缓存的Hibernate.这是一个处理音乐文件的应用程序(www.jthink.net/songkong),它使用Hibernate,因此它可以扩展更多数据,即它可以处理100,000首歌曲,内存比1000首歌曲更多.一旦处理完歌曲,那些歌曲就没兴趣了(除非用户运行撤销)

据我所知,如果我启用二级缓存(对于我的歌曲类),那么首先将歌曲写入缓存将使用更多的cpu然后如果只是写入数据库,并且对歌曲对象的其他修改也将需要更多的cpu资源.但是随后从Ehcache中检索歌曲将需要更少的资源,然后从数据库中检索它.

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

因此,没有特定的歌曲ID被检索1000次,但每首歌曲通常被写入1至4次并检索10次.因此,如果我们有一个非常小的缓存(因为我想保持堆内存受到严密控制)我会期望处理前几个文件夹将其歌曲添加到缓存中,然后当他们从新文件夹中完成歌曲时将采取他们的放在缓存中.

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

根据经验,10次检索与1-4次写入有意义使用二级缓存,或仅在比率更像100:1时才有用?

Hen*_*nri 4

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

写入堆缓存的成本并不高。所以,是的,即使从缓存中检索一次也会比返回数据库更快。

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

驱逐意味着您为缓存设置了一些最大大小。当达到这个值时,缓存将逐出“最旧的”条目以添加新条目。最古老的有多种定义。Ehcache 对一组条目进行采样,并踢出样本中最长时间未被访问的条目。

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

在您的情况下,您将需要加载该条目一次。然后将其放入缓存中。使用它并最后将其删除以节省内存。如果您知道在最后一步中不再需要该条目,只需将其删除即可。

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

您应该计算一个条目应在缓存中保留多少时间才能遍历所有执行器。这将是您的到期时间 (TTL)。然后您将缓存的大小或多或少设置为NB_EXECUTORS * NB_STEPS。它将是当前使用的歌曲的大小。添加新歌曲时,缓存将需要逐出旧条目。在大多数情况下,此条目将过期,因此不会造成任何损害。

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

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

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

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

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

这就是为什么我认为你应该直接缓存而不是使用二级缓存。但是,请小心,因为您会得到一个由 Hibernate 加载的对象。在将其放入缓存之前,您需要将其与 Hibernate 分离。然后将其附加到新的执行器中。否则,例如,如果您有收藏,可能会发生奇怪的事情。

现在,假设您希望每次都更新缓存和数据库。您有两种方法可以做到这一点。

使用 Cache-aside,您将更新数据库,然后更新缓存。

通过缓存直通,您将更新缓存,缓存将负责(原子地)更新数据库。CacheLoaderWriter由于您需要提供一个实现,因此缓存通过会涉及更多一些。但它确保缓存和数据库始终同步。