【问题标题】:How to lock objects before update using NHibernate lock modes?如何在使用 NHibernate 锁定模式更新之前锁定对象?
【发布时间】:2013-08-24 02:36:11
【问题描述】:

首先让我说明我要完成的工作。我有一个填充了工作的表。还有一个 Web 服务,其方法允许更改作业数据,称为SaveJob。此方法检索作业及其所有数据,对新数据运行验证(需要对其他表进行一些数据库查询),然后将其保存回数据库。它有点慢。大约一秒钟。

有时会发生两个SaveJob 调用会在同一个工作中靠近在一起,并且它们会相互覆盖。现在这是唯一的网络服务器,但我希望我的解决方案网络场兼容,所以即使我知道如何使用单例解决这个问题,我更愿意让数据库处理锁。

问题是当它尝试读取已被另一个 SaveJob 调用更改的作业时,我可以使用 NHibernate 和 SQL Server 数据库锁来阻止第二个 SaveJob 调用吗?

我相信答案是肯定的,但我不确定该怎么做。我已阅读有关ISession.Lock() 的文档,我相信我需要使用NHibernate.LockMode.Upgrade

我的下一个问题是这个锁什么时候释放?

我假设它在事务提交时被释放,但我找不到任何明确说明这一点的文档。

但是,如果是这种情况,那么有没有办法让我打开另一个事务并运行一些查询,而无需关闭我最初获得工作时开始的事务?还是我必须在一笔交易中完成这一切?

【问题讨论】:

    标签: .net nhibernate


    【解决方案1】:

    偶尔会发生两个 SaveJob 调用会紧挨在一起......

    这些作业指向同一个数据库行还是完全不同的实体?

    如果它们指向一个作业实例,您可以使用 version 的乐观锁定来禁止数据重写。

    如果它们不同,则只是允许并发查询执行的数据库点。 somehow slow 可以是关于表/索引/标识符锁定。

    不迟于提交后释放锁,取决于隔离级别。一些(读)锁在查询执行后立即释放,特定行内的更新可以在提交后释放。

    编辑:

    显然乐观锁对你不好,你需要使用

    //进行版本检查,使用 SELECT ... FOR UPDATE,然后重新关联: sess.Lock(pk, LockMode.Upgrade);

    http://nhibernate.info/doc/nh/en/index.html#manipulatingdata-update-lock

    【讨论】:

    • 只有当它们是相同的实体时才会出现问题。我已经编辑了我的问题以澄清这一点。
    【解决方案2】:

    首先,我建议不要跨多个线程使用单个事务,因为 ISession 本身不是线程安全的。

    当您在会话上或session.Get() 上使用LockMode.Upgrade 时,NHibernate 将在您检索实体时发出select with (rowlock) 语句(详细信息可能取决于数据库类型和配置)。如果对象先前被另一个线程检索过,则当前线程将等待直到锁被释放或超时到期。当update 语句在transaction.Commit() 上执行时,锁本身就会被释放。使用LockMode.UpgradeNoWait 提供了类似的功能,只是它不等待并且在获得锁定时会失败。

    这个Ayende's post 也有一些关于SQL NHibernate 在session.Lock() 上发出的信息。

    您可以在NHibernate Documentation Chapter 11, Section 6 上阅读不同类型的锁。

    【讨论】:

    • 谢谢!你能告诉我更多关于如何在一个线程中使用多个事务的信息吗?那可能吗?基本上我需要获得工作并保持该事务打开,然后我需要在不同的表上运行一些其他查询。我是否可以为此开立新交易?
    • 首先,更新应该发生在单个事务中,包括获取对象、更新属性和调用更新,或者您可以在分离的实体上调用session.Merge()。在任何情况下,您都不需要为此进行多次交易。不过,我什至会在尝试更新实体之前进行验证,因为只为新更新无法验证而获取对象似乎效率低下。
    • 还有一点,您不需要事务来验证对象,但无论哪种情况,您都应该在之前的事务中进行,然后只需打开一个新的会话/事务。你不要这些打开太久。
    • 我必须对作业数据进行的验证需要我进行额外的数据库获取调用。一些验证依赖于数据库中的数据。我可以在不干扰同一线程上的作业获取/编辑事务的情况下“推送”另一个事务或启动另一个事务吗?
    • 你可以打开一个事务来检索验证所需的数据,关闭事务/会话,(就像我说的,你不需要一个事务,只是一个会话,尽管有些人可能会推荐在只读操作上使用事务),进行验证,然后打开另一个事务以保存更新。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-02
    • 1970-01-01
    相关资源
    最近更新 更多