【问题标题】:Concurrency issues with multiple, independent database transactions?多个独立数据库事务的并发问题?
【发布时间】:2012-02-13 22:58:07
【问题描述】:

您将如何使用以下代码解决并发问题?在此示例中,我们想知道为什么用户身份验证失败。问题是这段代码对数据库进行了两次单独的调用,但我们希望整个方法发生在概念事务中。具体来说,我们对isolation 感兴趣。在我们确定身份验证失败的原因之前,我们不希望在执行此方法期间的并发写入影响我们的读取。

我想到了几个解决方案:Thread LockingTransactionScopeOptimistic Locking。我真的很喜欢乐观锁定的想法,因为我认为冲突可能很少见,但是 .NET 中没有内置的东西可以做到这一点,对吧?

另外 - 在这种情况下,这真的值得关注吗?什么时候考虑这样的并发问题很重要,什么时候不重要?在实施解决方案时需要考虑什么?表现?锁定的持续时间?发生冲突的可能性有多大?

编辑:在查看了 Aristos 的回答后,我认为我真正想要的是 Authenticate 方法的某种“snapshot”隔离级别。

public MembershipStatus Authenticate(string username, string password)
    {
        MembershipUser user = Membership.GetUser(username);
        if (user == null)
        {
            // user did not exist as of Membership.GetUser
            return MembershipStatus.InvalidUsername;
        }

        if (user.IsLockedOut)
        {
            // user was locked out as of Membership.GetUser
            return MembershipStatus.AccountLockedOut;
        }

        if (Membership.ValidateUser(username, password))
        {
            // user was valid as of Membership.ValidateUser
            return MembershipStatus.Valid;
        }

        // user was not valid as of Membership.ValidateUser BUT we don't really
        // know why because we don't have ISOLATION.  The user's status may have changed
        // between the call to Membership.GetUser and Membership.ValidateUser.
        return MembershipStatus.InvalidPassword;
    }

【问题讨论】:

  • 我不确定您是否真的需要担心这里的隔离问题。在调用GetUserValidateUser 之间,用户被删除或锁定的可能性有多大?更重要的是,您不应该将这些信息暴露给用户。如果您针对“密码错误”、“用户不存在”和“帐户被锁定”显示不同的消息,则试图入侵您网站的人可能会利用该信息获取有效用户名列表。您不希望这样,因为这些用户名可能有助于利用您的基础架构中的其他漏洞。
  • +1 表示响应。我可能在这里使用了一个糟糕的例子(尽管我计划区分“无效凭据”和“帐户被锁定”,也许我需要重新考虑)。我只是不喜欢数据可以在我下面改变的事实!并发难!如果我需要数据保持一致怎么办,您有什么建议?

标签: c# asp.net database concurrency sqlmembershipprovider


【解决方案1】:

根据我对herehere 的阅读,似乎System.Transactions.TransactionScope 包装了您的整个方法应该自动将您的数据库调用加入到一个公共事务中,从而在整个事务中实现事务安全整个交易范围。

你会想做这样的事情:

public MembershipStatus Authenticate(string username, string password)
{
    using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.Snapshot }))
    {
    MembershipUser user = Membership.GetUser(username);
        if (user == null)
        {
            // user did not exist as of Membership.GetUser
            return MembershipStatus.InvalidUsername;
        }

        if (user.IsLockedOut)
        {
            // user was locked out as of Membership.GetUser
            return MembershipStatus.AccountLockedOut;
        }

        if (Membership.ValidateUser(username, password))
        {
            // user was valid as of Membership.ValidateUser
            return MembershipStatus.Valid;
        }

        // user was not valid as of Membership.ValidateUser BUT we don't really
        // know why because we don't have ISOLATION.  The user's status may have changed
        // between the call to Membership.GetUser and Membership.ValidateUser.
        return MembershipStatus.InvalidPassword;
    }
}

【讨论】:

  • +1 花时间回复。为什么我需要实现自己的会员提供程序?难道我不能只使用 TransactionScope 类“其中事务由基础设施自动管理”吗?
  • @Brandon 抱歉花了这么长时间才回复——我想在回答这个问题之前做一些研究。我已经更新了我的答案以反映 TransactionScope 的使用。
  • 感谢您的回复。我将此标记为已接受的答案,主要是为了您对我的问题的第一条评论(我可能一开始就不应该这样做,即使我想这样做也不是问题)。
【解决方案2】:

我将使用mutex 名称作为锁定参数,因此只有同一个用户可以锁定一段时间。这对我来说对一台计算机来说更安全,因为使用互斥锁我可以从不同的池或网络调用中捕获所有可能的线程。

public MembershipStatus AuthenticateLock(string username, string password)
{
    if(string.IsNullOrEmpty(username))
      return MembershipStatus.InvalidUsername;

    // TODO: Here you must check and clear for non valid characters on mutex name
    using (var mutex = new Mutex (false, username))
    {
         // possible lock and wait, more than 16 seconds and the user can go...
         mutex.WaitOne (TimeSpan.FromSeconds(16), false);

         // here I call your function anyway ! and what ever done...
         //  at least I get a result
         return Authenticate(username, password)
    }
}

更多 cmets:Membership.ValidateUserMembership.GetUser 都调用数据库。

但是如果您对进行此调用并影响此参数的页面使用标准的 asp.net 会话那么页面都准备好锁定另一个,所以我认为那里没有机会需要这个互斥调用。因为会话的锁足以同步这部分。我提醒一下,会话正在为所有用户从头到尾锁定页面。

关于会话锁定: Replacing ASP.Net's session entirely

jQuery Ajax calls to web service seem to be synchronous

【讨论】:

  • +1 花时间回答这个问题和会话信息。所以你建议线程锁定。线程锁定确实提供了一种“软”隔离,但如果您有多个进程(不是线程)试图访问共享资源,则不会有帮助。我认为我真正追求的是 Authenticate 方法的某种“快照”隔离级别。
  • @Brandon 我在这里使用的互斥锁对多个进程很有帮助!但是我说你甚至不需要这个,如果你在对这个函数的所有调用中都使用 session。会话锁定所有准备好的页面,因此您不需要第二次锁定。
  • 有趣!我不知道“命名系统互斥体在整个操作系统中都是可见的,并且可以用于同步进程的活动。”所以我猜边界是 PC/OS,例如您不能跨多个 PC/OS 使用互斥锁同步进程?
猜你喜欢
  • 2014-04-26
  • 2022-01-23
  • 2015-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多