我们有一个高吞吐量的只读 spring/jpa/hibernate 应用程序(Web API),并发现当依赖 Hibernate 的二级缓存时性能不可接受 - 基本上太多的 CPU 时间花在 Session 上的每个读取事务中重新水合实体上。得到()。
因此,我们正在将(在 VM 内)实体缓存为对象(非脱水),因为:
- 我们可以将整个数据模型放入 VM 堆中
- 我们不关心过时(可以假设缓存代表应用程序生命周期内的最新状态)
最初我们想实现一个直写缓存层,它位于服务层和 DAO 层之间。
但是,考虑到上述假设,另一种方法似乎更简单:
声明一个会话EntityManager(在应用程序启动时初始化,在应用程序停止时手动关闭):
@PersistenceContext(type = PersistenceContextType.EXTENDED, properties = {@PersistenceProperty(name = AvailableSettings.FLUSH_MODE, value = "MANUAL")}) private HibernateEntityManager conversationalEM;
将上述代理注入所有 DAO 实现
- (可选)在应用程序启动时在单独的事务中将所有实体预加载到 EM 中(以避免多个线程随后尝试写入同一会话的可能性)
- 因此,像 dao.loadEntity(id) 这样的所有操作都会“命中”共享 Hibernate Session 并返回实体实例而无需数据库查询或重新水化
后一种方法有什么陷阱吗?
例如,EM 将跨越许多(总是只读的)事务,包括并发(多个客户端同时访问 API),并且这些事务仍然必须提交。由于 Hibernate EM impl 引用了 tx 对象,似乎人们可能会遇到这样一种情况,即多个线程在同一个 TX 对象上尝试 commit() ......