我有一个相当简单的数据模型,如:
class MyParent {
// 7 fields here, some numeric, some String, not longer than 50 chars total
Set<MyChild> children;
}
class MyChild {
int ownerId;
// 3 more fields, numeric or dates
}
MyParent
,MyChild
和MyParent.children
都使用read-only
缓存。
我有40,000个MyParent
个实例和100,000个MyChild
个实例。这会在缓存中产生180,000个条目(如果你添加40,000 MyParent.children
)。
我想要缓存所有内容,按ownerId
分组。我不想重新发明轮子,我想使用查询缓存,如:
Query query = session
.createQuery(
"select distinct p from MyParent p join fetch p.children c where c.ownerId = :ownerId");
query.setParameter("ownerId", ownerId);
query.setCacheable(true);
query.setCacheRegion("MyRegion");
query.list();
对于ownerId
的所有1,500个值。
缓存有效,但我注意到它很大!用Ehcache.calculateInMemorySize()
测量,平均每个条目超过一千字节大。为了缓存~180,000个条目,我需要超过200 MB。考虑到参赛作品本身要小得多,那就太过分了。
开销来自何处以及如何减少开销?
答案 0 :(得分:2)
我不确定你用什么缓存做数学运算,但让我用MyParent类作为例子。根据您对该类的解释,在启用了compressOops的64位VM上,MyParent实例将在堆中低于500字节。那就是没有Set,我将在后面解释原因(否则它将是另外128个字节)。缓存还需要保存该条目的密钥,该密钥会添加到计算中...
Hibernate不会直接使用主键将其存储在缓存中的键,而是使用CacheKey条目。该实例保存值表示的实体的pk以及其他四个字段:type,Hibernate类型映射; entityOrRoleName,实体或集合角色名称; tenantId,与此数据关联的租户标识符;最后,pk的hashCode(参见org.hibernate.type.Type.getHashCode)。
现在遗憾的是,这一切都没有结束,该条目的值不是MyParent实例,而是CacheEntry实例。这次,除了更多的元数据(subClass,实体名称,默认为FQCN; lazyPropertiesAreUnfetched,布尔值;以及实体中的optimisitc锁定值)之外,该实例仍然不包含MyParent实例,而是一个反汇编的表示形式它。此表示形式是实体的状态(所有属性)的数组。
我想通过这些信息,你的hibernate缓存的“估计”大小将更有意义。我想强调的是,这些只是估计,如果我没记错,它的计算方法可能略高于现实。实际上,例如,CacheKey中的一些信息可能应该以不同的方式计算。从Ehcache 2.5开始,您将能够在Caches上启用基于内存的调优(甚至在CacheManager级别)。完成后,将精确测量缓存条目,calculateInMemorySize()将为您提供缓存的实际测量大小。
您现在可以从ehcache.org下载2.5版的测试版。另请注意,在缓存中使用基于字节的大小调整时,调整引擎将在Hibernate缓存类型中的缓存条目中考虑这些共享实例。您可以在此处详细了解这一切的工作方式:http://ehcache.org/documentation/configuration.html#Memory_Based_Cache_Sizing_Ehcache_2.5_and_higher
希望能帮助你更好地理解这一切...... 亚历克斯