asp.net - 休眠死锁

标签 asp.net sql-server nhibernate

我在 ASP.NET 页面中使用以下代码来创建记录,然后对记录进行计数以确保没有超出设定的限制,如果超出则回滚事务。

using (var session = NhibernateHelper.OpenSession())
using (var transaction = session.BeginTransaction())
{
    session.Lock(mall, LockMode.None);

    var voucher = new Voucher();
    voucher.FirstName = firstName ?? string.Empty;
    voucher.LastName = lastName ?? string.Empty;
    voucher.Address = address ?? string.Empty;
    voucher.Address2 = address2 ?? string.Empty;
    voucher.City = city ?? string.Empty;
    voucher.State = state ?? string.Empty;
    voucher.Zip = zip ?? string.Empty;
    voucher.Email = email ?? string.Empty;
    voucher.Mall = mall;
    session.Save(voucher);

    var issued = session.CreateCriteria<Voucher>()
        .Add(Restrictions.Eq("Mall", mall))
        .SetProjection(Projections.Count("ID"))
        .UniqueResult<int>();

    if (issued >= mall.TotalVouchers)
    {
        transaction.Rollback();
        throw new VoucherLimitException();
    }

    transaction.Commit();
    return voucher;
}        

但是,我遇到了很多僵局。我认为发生这种情况是因为我试图对刚刚执行插入的表中的记录进行计数,并且插入的行上仍然持有锁,从而导致死锁。

  • 谁能证实这一点?
  • 有人可以提出修复建议吗?

我尝试在最终查询中调用 SetLockMode(LockMode.None),但这会导致我无法弄清楚的 NullReferenceException。

编辑:如果我在保存对象之前运行查询,它会起作用,但随后我无法实现验证我的插入未以某种方式超出限制的目标(在并发插入的情况下)。

编辑:我发现在 session.BeginTransaction 调用中使用 IsolationLevel.ReadUncommited 可以解决问题,但我不是数据库专家。这是问题的适当解决方案还是我应该如何调整我的逻辑?

最佳答案

这种设计很容易出现死锁 - 通常(并非总是)一个连接本身不太可能发生死锁,但对同一个表进行插入和聚合的多个连接很可能发生死锁。这是因为,虽然从执行工作的连接的角度来看,一个事务中的所有事件看起来都是完整的 - 数据库不会将事务锁定在“其自己的”记录之外 - 来自其他事务的聚合查询将尝试锁定同时处理整个表或其中的大部分,这会导致死锁。

在这种情况下,读取未提交不是你的 friend ,因为它基本上表示“忽略锁定”,这在某些时候意味着违反你围绕数据设置的规则。 IE。表中的记录计数将不准确,您将根据该不准确的计数采取行动。当真实答案是 11 时,您的计数将返回 10 或 13。

我最好的建议是重新安排插入逻辑,以便捕获计数的想法,而不是逐字计算行数。你可以走几个方向。我的一个想法是:用一个序列对插入的凭证进行逐字编号,并对序列本身施加限制。

  1. 制作一个包含列(我猜)MallID、nextVoucher、maxVouchers 的序列表
  2. 在该表中添加 mallids、1 以及每个购物中心的限制
  3. 将插入逻辑更改为以下伪代码:
Begin Transaction
Sanity check the nextVoucher for Mall in the sequence table; if too many exist abort
If less than MaxVouchers for Mall then {
  check, fetch, lock and increment nextVoucher
  if increment was successful then use the value of nextVoucher to perform your insert. 
    Include it in the target table.
}
Error? Rollback
No Error? Commit

像这样的序列表会损害一些并发性,但我认为不如不断计算表中的行那么严重。请务必进行性能测试。 此外,[检查、获取、锁定和增量]也很重要 - 您必须独占锁定序列表中的行,以防止其他连接在增量之前瞬间使用相同的值。我知道这方面的 SQL 语法,但恐怕我不是 nHibernate 专家。

对于读取未提交数据错误,请检查:http://sqlblog.com/blogs/merrill_aldrich/archive/2009/07/29/transaction-isolation-dirty-reads-deadlocks-demo.aspx (免责声明:Merrill Aldrich 就是我:-)

关于asp.net - 休眠死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1229617/

相关文章:

nhibernate - Nhibernate 中多对多集合的更新导致连接表中的多次删除

c# - 从 Blob Azure 下载重定向错误

sql - 在 INNER JOIN 条件下使用 'OR' 是一个坏主意吗?

c# - 我可以从构建中排除 Microsoft.Data.OData 语言资源吗?

sql - 计算切换间隔 SQL Server 的秒数

sql-server - SQL Server : time-series data performance

c# - 有没有办法让 NHibernate 将字符串键视为不区分大小写?

.net - 为 .NET 项目选择数据库和 ORM

asp.net - 防止回发出现在历史记录中

asp.net - 该模型项的类型为CookMeIndexViewModel,但需要一个模型项的类型为IEnumerable <CookMeIndexViewModel>