(N)Hibernate“每个应用程序会话”被认为是特定用例的邪恶?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(N)Hibernate“每个应用程序会话”被认为是特定用例的邪恶?相关的知识,希望对你有一定的参考价值。

好的,每个人都知道不鼓励使用(N)Hibernate的每个应用程序的全局会话。但是我有一个非常具体的,显然是非标准的用例,它似乎是理想的解决方案。

总而言之,我的(服务器)应用程序基本上将所有持久数据保持在内存中,并且从不查询数据库以进行正常操作。首先是数据库的唯一原因是数据在过程的生命周期中存活。我只想在应用程序启动时查询数据库以将所有内容提取到内存中。数据库实际上只有5-10 MB左右。

现在的问题是,如果我遵循会话必须短暂的建议,我必须为每个业务事务合并()我的所有数据或以某种方式手动跟踪所有更改,而不是利用NHibernate的自动更改跟踪。这使得持久性很难实现而不会导致很多性能开销。

所以我的问题是,为什么我不应该为这个特定的用例使用全局会话?

我所知道的反对全球会议的常见论点:

  1. 随着时间的推移,第一级缓存将被整个数据库填充=>我不介意,因为我实际上想要将所有数据都存储在内存中!
  2. 陈旧的数据和并发问题=>我的应用程序的设计使得所有可以访问或修改持久数据的代码必须是单线程的(有意的设计选择),并且它是唯一可以写入数据库的应用程序。所以这应该不是问题。
  3. 如果会引发异常(例如,数据库超时),则会被破坏=>这是我能看到的唯一真正的问题,但可以通过丢弃会话,创建新会话并刷新所有数据来解决。价格昂贵,但例外情况应该非常罕见,只能由主要错误或主要基础设施问题引起,这些问题都应该尽快解决。

所以我认为没有理由不为我的特定用例使用全局会话。或者有什么重要的东西我不见了?

更新1:这是一个服务器应用程序

更新2:这并不意味着长期存在的全球交易。交易仍然是短暂的 - 一个长期会话,许多短期交易。

答案

如果您将来自多个线程的所有事务扇入到单个专用后端线程执行程序中,那么您确实可以为每个应用程序使用单个会话。

锁定超时,服务器崩溃或约束违规可能会触发异常,因此撤消备份会话会导致丢弃所有第一级缓存条目,这对您的用例不利。在这种情况下,您将不得不从数据库中重新获取所有内容,并且因为您使用单个后端线程,所有其他客户端线程将被阻止,这是不能令人信服的。

我会建议你使用second-level cache instead。您可以将2LC提供程序配置为在内存中进行扩展,而不是溢出到磁盘。您可以在应用程序启动时加载二级缓存中的所有数据,并使用NONSTRICT_READ_WRITE Cache Concurrency Strategy来加速写入(无论如何,并发问题对您来说都不是问题)。

你需要确保你使用2NL caching for collections too

最简单的设计是使用每个请求的会话,因为Session无论如何都是轻量级的,它无论如何都将从内存中的2LC获取数据。

您需要运行一些性能测试,以查看是否值得重用Session,而不是在每个事务上创建一个新的。您可能会发现此过程不是您的瓶颈,如果没有真正的证据,您不应该进行任何优化。

丢弃会话的另一个原因是大多数与数据库相关的异常无论如何都无法恢复。如果服务器关闭或当前请求引发约束违规,则重新安装它将无法解决任何问题。

另一答案

我可以看到的一个潜在缺点是脏检查可能需要很长时间才能执行;您将不得不使用字节码检测模式来解决此问题。

此外,对服务器的序列化访问可能会比从二级缓存重新创建对象更多地影响性能(在现代JVM中对象创建速度非常快)。在单用户应用程序中甚至是这样(用户可能在一个屏幕上触发长时间运行并希望在另一个屏幕上执行其他操作,或者服务器可能触发预定操作,从而阻止用户访问服务器,直到操作完成)。

第三,如果最终需要执行并发请求,那么稍后重新构建您的方法可能会很困难。

接下来,无论如何都不会避免在执行查询时进入数据库。

最后,扩展会话是一种Hibernate功能,它不像常规的每个请求会话模式那样常用。虽然Hibernate是一个很好的软件,但它也很复杂,因此有很多bug。我期望更多的错误和较弱的社区支持/文档​​与较少使用的功能相关(如在其他所有框架中)。

所以我的建议是使用二级缓存并根据用例使用乐观/悲观锁来处理并发问题。默认情况下,您可以使用DISABLE_SELECTIVEALL共享缓存模式启用所有实体的缓存,如docs中所述。

另一答案

不使用全局会话的原因可归纳如下:

*一级缓存:你必须要了解。一级缓存不仅仅是关于内存。第一级缓存的结果是当对象被保存或删除或查询时,(n)hibernate必须确保在此事件之前,数据库必须与您的内存保持一致状态。那是昙花一现。但是,(n)hibernate有一个独特的功能,称为透明持久性。与其他ORM的实体框架不同,您没有跟踪什么是脏的。 NHiberante为您做到了这一点。但它的工作方式有点昂贵。它将所有先前的对象状态与新对象状态进行比较,并尝试检测已发生变化的情况。因此,如果您的第一级缓存中充满了实体,性能将会降低。可以通过两种方式规避这个问题:

1-)使用session.Flush(); session.Clear();

2-)使用无状态会话。

在第一种情况下,所有挂起的更改都将转到数据库。之后,您可以安全地清除会话。但是,即使您清除会话直到事务处理完毕,也会将数据保留在内存中。这是因为你可以在最后投票决定被拒绝。 (如果要求,可以提供更多信息)

第二种情况,对于无状态会话,(n)hibernate的行为类似于微型ORM。没有跟踪。它重量轻但却给你更多的责任。

*与会话相关的异常:不使用应用程序范围会话的另一个有效原因是,每当发生与会话或数据库相关的异常(例如唯一约束违规)时,您的会话就注定失败。您无法再使用它,因为它与数据库处于不一致状态。因此,您的全球会话必须更新,这会带来更多复杂性。

*线程安全:会话和任何ADO.NET构造都不是线程安全的。如果您使用全局会话对象以及多个线程,则必须确保某种线程安全性。这可能非常困难。

以上是关于(N)Hibernate“每个应用程序会话”被认为是特定用例的邪恶?的主要内容,如果未能解决你的问题,请参考以下文章

hibernate n+1问题

一般来说,哪个 Java ORM 被认为是性能最高的? [关闭]

Hibernate @Temporal的使用

EclipseLink 相当于 Hibernate 的 NaturalID 概念

“\n”被认为是一个或两个字符,它可以存储在一个字符中吗?

Hibernate 3.x vs 4.x性能视角