什么时候将对象添加到Hibernate 2级对象缓存中会给CPU带来的好处超过初始命中率。
我当前正在使用没有二级缓存的Hibernate。这是用于处理音乐文件(www.jthink.net/songkong)的应用程序,它使用Hibernate,因此可以扩展更多数据,即可以处理100,000首歌曲,而存储的内存比1000首歌曲少。处理完歌曲后,这些歌曲就不再引起关注(除非用户运行“撤消”操作)
据我了解,如果启用(针对我的歌曲类的)二级缓存,则将歌曲初次写入缓存将使用更多的cpu,而不是仅写入数据库,并且对歌曲对象的其他修改也将需要更多cpu资源。但是,随后从Ehcache中检索歌曲时,与从数据库中检索歌曲相比,所需资源将更少。
我的歌曲逐个文件夹地处理,并且经历了多个阶段(在不同的执行器上),当它们在下一个执行器上排队时,我们只是将歌曲ID作为参数传递,否则,它将使用大量的堆内存存储歌曲本身就是对象。因此,当特定任务实际在Executor上运行时,它要做的第一件事就是检索这些ID的歌曲。
因此,没有特定的歌曲ID可以被检索1000次,但是每首歌曲通常被写入1至4次并被检索10次。因此,如果我们的缓存非常小(因为我想让堆内存处于紧密控制之下),我希望处理前几个文件夹将其歌曲添加到缓存中,那么当它们完成新文件夹中的歌曲时,它们就会放在缓存中。
但是我的问题是,这值得吗?
根据经验,使用10次检索相对于1-4次写入可以使用二级缓存,或者仅当比率更像是100:1时才有用吗?
答案 0 :(得分:4)
真正的答案是:只需对其进行基准测试。
写到堆缓存并不昂贵。因此,是的,即使从缓存中检索一次,也可以使其更快然后重新回到数据库。
然后,缓存在HashMap之上主要执行两件事。它逐出并过期。
逐出表示您为缓存设置了一些最大大小。达到此目的后,缓存将逐出“最旧”条目以添加新条目。对于最早的有多个定义。 Ehcache对一组条目进行采样,并踢出样本中时间最长的未访问条目。
到期意味着给定的条目将在某些时候被认为是陈旧的。例如,您要保留一个条目1小时,然后再使用数据库中的最新条目刷新该条目。收到条目时,Ehcache首先查看条目是否过期。如果是,它将返回null并从缓存中删除该条目。这意味着过期的条目将保留在缓存中,直到您尝试访问它为止。
在您的情况下,您将希望加载一次条目。然后将其保存在缓存中。使用它,最后将其删除以节省内存。如果您最后一步知道不再需要该条目,只需将其删除。
如果您不这样做,则必须依靠搬迁。因为驱逐算法将首先删除过期的条目(如果可以删除过期的条目,为什么要删除一个完全有效的条目?)。
您应该计算条目应保留在缓存中多少时间才能通过所有执行器。这将是您的到期时间(TTL)。然后,将缓存的大小或多或少地设置为NB_EXECUTORS * NB_STEPS
。然后将是当前使用的歌曲的大小。添加新歌曲时,缓存将需要逐出旧条目。在大多数情况下,该条目将过期,因此不会造成危害。
为防止驱逐(在找不到过期的条目时可能会付出高昂的代价),可以编写一个获取条目的后台例程。它将触发到期。但同样,在使用基准确定它实际上更快之前,请不要这样做。
最后,您可能希望直接缓存歌曲,而不是使用Hibernate 2级。因为获取歌曲所需的操作较少。同样,当写入位于二级缓存中的条目时,Hibernate倾向于从缓存中退出。确保将其配置为不。
有关修改的说明。默认情况下,每个引用都是Ehcache堆上缓存(并且只有堆上缓存)。因此,如果您从缓存中检索Song对象,然后对其进行修改,那么缓存中的条目也会被修改,因为它实际上是唯一的实例。
但是,这不是Hibernate二级缓存的工作方式。他们将在缓存中保留某种数据库行。这将转换为歌曲并返回给您。
当您将歌曲保存到数据库中时,Hibernate会像我上面所说的那样从缓存中逐出它(但是您可能会要求在配置中更新缓存,我不确定)。
这就是为什么我认为您应该直接缓存而不是使用二级缓存。但是,请注意,因为您会由Hibernate加载对象。您需要先将其与Hibernate分离,然后再将其放入缓存。然后将其附加到新的执行程序中。否则,例如,如果您有收藏夹,可能会发生奇怪的事情。
现在,假设您想每次更新缓存和数据库。您有两种方法可以做到。
在不使用缓存的情况下,您将更新数据库,然后更新缓存。
通过高速缓存,您将更新高速缓存,这将(原子上)小心地更新数据库。由于您需要提供CacheLoaderWriter
实现,因此需要更多地涉及高速缓存。但这可以确保缓存和数据库始终保持同步。