【问题标题】:Best practices to avoid pross-thread/process database accessment避免跨线程/进程数据库评估的最佳实践
【发布时间】:2012-10-15 14:05:37
【问题描述】:

我正在开发 MVC Web 应用程序。

这意味着我正在创建视图、模型、视图模型。我使用 linq-to-sql 作为数据库提供程序,我已经实现了自定义 Unit-of-Work 模式,以及看起来完美的数据源、存储库和服务模式(在我的实现中)并且与直接的 SQL 代码完全分离。实际上,来自任何为单元测试而设计的数据库,因此我可以使用内存数据源测试我的应用程序,而不会影响数据库。

最终我遇到了一个问题:我没有针对跨线程(或跨进程,因为据我所知,IIS 可以使用单个 Web 应用程序创建多个应用程序域)操作的保护。

例如,我有一个包含一些记录的表。并且不时发生一个 Web 请求(在控制器内部,然后是服务,然后是存储库)选择 SQL 表的最大行,比如说 TicketId 列,然后在该表中插入另一行 (that column value + 1)

如果两个或多个线程或进程做同样的事情,重复的值可能会出现在数据库中。前段时间,当我的 web 应用程序更小时,我在 SELECT 语句中使用了直接 SQL 代码和简单的 UPDLOCK TransactionScope using 块锁定了我正在修改的记录(或其他任何东西)阻止所有其他数据库客户端等到我完成。

对于所有这些模式,我忘记了一件事:
我如何实际实施数据库多访问保护问题
没有任何直接的 SQL 代码。

【问题讨论】:

  • 数据库身份仍应由数据库管理,让它做+1。它的算法已经过很好的测试:)
  • 什么算法?我的存储库可以同时运行,将重复值生成到数据库中,并在同一个项目中工作两次。当两个相同的网络请求进来时就会发生这种情况。
  • 如果您让数据库生成 ID,则不会。这就是 SQL Server 的 IDENTITY 特性存在的原因。
  • 我并不总是需要 +1。在其他 50% 的情况下,我需要将 TicketId 的值保持在与之前所有增加 id 列相同的最大值(你们都在寻找我的身份)

标签: c# sql-server design-patterns linq-to-sql multithreading


【解决方案1】:

我如何实际实现数据库多访问保护问题?

这是数据库引擎的工作。如果报告了任何问题,您的工作是确保您的应用程序正常失败。见Locking in the Database Engine

例如,我有一个包含一些记录的表。并且时不时会发生一个网络请求 (在控制器内部,然后是服务,然后是存储库)在假设 TicketId 列的最大值 > 上选择 SQL 表的行,然后在该表中插入具有(该列值 + 1)的另一行。

我在这里得到的印象是,考虑到您正在尝试复制它的行为,您似乎对您的数据库没有太大的信心。将您的字段设置为auto-increment,这应该可以解决您的问题。如果您确实必须实现自己的手动自动增量,那么您需要使用某种形式的锁定,因为您本质上拥有的是竞争条件,例如

private static object lockObject = new Object();

lock(lockObject)
{
    ... DB stuff
}

对于跨进程锁定,您可能需要查看名为Mutex

【讨论】:

  • 那么它是如何知道何时何地放置锁的呢?
  • 在某些情况下我需要使用重复值。在其他一些情况下,我没有。这就是为什么我有时只需要+1
  • Locks 不会阻止代码同时在两个或多个应用程序域中运行。
  • 据我所知,IIS 不会为单个站点实例创建多个 AppDomain。您是否读过其他内容?
  • 可能有多个实例,但在不同的进程中。首先,如果开启了app pool的Overlapped Recycle选项(并且默认开启),那么在发生回收时可能会有多个w3wp进程。如果 Maximum Worker Processes 选项大于 1,也可能有超过 1 个实例。
【解决方案2】:

为什么不用一张表来表示一个票号和Seriliazable transaction isolation level的使用交易呢?

int GetTicketNumber(bool shouldIncrement)
{

   if(shouldIncrement)
   {
     // use transaction with serilizable isolation level
     select and update
   }
   else
   {
     //just select
   }

}

【讨论】:

  • 在您的示例中,SELECT 子句(由 Linq-to-SQL 代码生成,由我的存储库调用)会放置某种锁吗?这样其他应用程序在事务提交或处置之前无法访问该表?
  • 是的,我认为这行得通;事务隔离级别 " SERIALIZABLE 指定以下内容:语句无法读取已被其他事务修改但尚未提交的数据。在当前事务完成之前,其他事务不能修改当前事务已读取的数据。" msdn.microsoft.com/en-us/library/ms173763.aspx
  • 那是我要找的锁。谢谢。
猜你喜欢
  • 1970-01-01
  • 2010-10-14
  • 2013-02-03
  • 1970-01-01
  • 2010-10-08
  • 1970-01-01
  • 1970-01-01
  • 2019-04-03
  • 1970-01-01
相关资源
最近更新 更多