SqlConnection无法打开

4

我有一个程序,可以并行地向 SQL Server 实例发送多个请求。在所有的并发请求中,其中一个请求总是在 db.open() 上阻塞几秒钟。

void MyMethod()
{
    var brands = new  List<string>{"Chevy", "Honda", "Ford", "GM"};
    var foundCars = new  ConcurrentBag<Car>();   

    Parallel.ForEach(brands, brand =>
    {
      logger.Trace(brand + " before enqueue");
      foundsCars.Enqueue(FindCar(brand));
      logger.Trace(brand + " after enqueue");
    });
}

public Car FindCars(string brand)
{
   using (var db = new SqlConnection(connectionString))
   {
     sqlLogger.Trace("Brand " + " brand " + " before db open");
     db.Open();
     sqlLogger.Trace("Brand " + " brand " + " after db open");
     using (var cmd = new SqlCommand(sqlCmd, db))
     {
        while (reader.Read())
        {              
            //SQL SELECT Stuff going on
        }
     }
   }
}

如果我查看我的跟踪记录,可能会看到类似这样的内容,顺序取决于数据库响应时间,我想,在这种情况下 Honda 是正在缓慢处理的数据,但并不总是这样:

12:00:00.0000 Chevy before enqueue 
12:00:00.0000 Honda before enqueue 
12:00:00.0000 Ford before enqueue
12:00:00.0000 GM before enqueue
12:00:00.0200 Brand Chevy before db open
12:00:00.0300 Brand Honda before db open
12:00:00.0200 Brand Ford before db open
12:00:00.0200 Brand GM before db open
12:00:00.0300 Brand Chevy after db open
12:00:00.0300 Brand Ford after db open
12:00:00.0300 Brand GM after db open
12:00:00.0400 Chevy after enqueue
12:00:00.0400 Ford after enqueue
12:00:00.0400 GM after enqueue
12:00:07.0000 Brand Honda after db open <-- usually around 7 seconds late
12:00:07.0100 Honda after enqueue

我无法在我的SQL Server 2008实例上复制此问题,但它在SQL Server 2012实例上总是发生。这个问题似乎是一个配置问题。当我发送并发请求且SELECT请求时,它总是发生。如果我在SQL Server Management中执行阻止查询,则会在子100ms内返回结果。

这是我的连接字符串:

<add key="SqlConnectionString" value="Data Source=10.0.20.20;Initial Catalog=CarsDB;Integrated Security=SSPI;Connection Timeout=240;" />

编辑:

我在连接字符串中插入了“Min Pool Size=30”,似乎只有第一次、第二次或第三次调用该方法时耗时超过15秒,然后所有其他方法调用都在500毫秒以下。

我还在我们的测试环境中运行了SQL Server 2012,并在其中使用与出现问题的表中相同的元数据,但我无法复制此问题。看起来是某个地方的配置问题。


你能否添加一个调试行,显示数据库连接离开 using 语句后的时间。using (var db = new SqlConnection(connectionString)){ /*...*/} sqlLogger.Trace("Brand " + " brand " + " after db close"); 然后更新你的问题,列出语句的顺序? - Scott Chamberlain
这是真的吗?你真的认为对同一张表进行多个并行连接会使SQL运行更快吗?在另一端是一组单一的物理磁头。你真的认为多个连接是资源的好用处吗?如果有五个老板同时告诉你他们需要什么,这是否意味着你可以工作5倍快? - paparazzo
@Blam 实际应用程序中存在更复杂的业务逻辑,但是也许我可以将所有 20 次调用捆绑在一起。 - guiomie
1
单个物理写头的哪一部分不清楚?Bundle?首先要摆脱Parallel.ForEach。 - paparazzo
有可能在“本田”查询之前的某个调用正在锁定一个包含“本田”查询所依赖的重型查询的表格吗?如果您将日志中的查询转储出来,然后作为一个大脚本在本地 SQL Server 上运行,会发生什么?问题是否会复制? - Paurian
显示剩余3条评论
4个回答

1
除非您有分区表,否则这是荒谬的。
Parallel.ForEach(brands, brand =>
{
  logger.Trace(brand + " before enqueue");
  foundsCars.Enqueue(FindCar(brand));
  logger.Trace(brand + " after enqueue");
});

多个连接同时访问同一张表并不能提高速度,更不用说多个连接的开销了。而且一个物理读写头只有一个。你难道没有意识到打开和关闭连接所花费的时间比简单的读取要长得多吗?实际上,你只需要排队读取表中的数据。读取器非常非常快。


0

将连接字符串中的集成安全从SSPI切换到用户/密码修复了该问题。看起来我们的一个环境存在Active Directory问题,而另一个环境则没有。


0

尝试在您的ConnectionString中使用MARS。它应该看起来像这样:Data Source=10.0.20.20;Initial Catalog=CarsDB;Integrated Security=SSPI;Connection Timeout=240;MultipleActiveResultSets=True;


MARS 不会影响他正在做的事情。他每个连接只有一个活动读取器。 - Scott Chamberlain

0

看起来是连接池耗尽了。Parallel.ForEach 很容易产生大量线程的垃圾邮件。根据我的观察,对于某些 IO 工作负载来说,实际上是没有限制的。将并行度限制在一个合理的值。


我使用了与WhenAll()结合的Tasks,逻辑类似,但问题仍然存在。 - guiomie
有多少个任务?这会更容易出现疲劳,因为没有任何限制。 - usr
我们正在谈论20到40个并发任务,根据我的过去经验,这并不算太多。你觉得呢? - guiomie
默认的线程池大小为100,因此你的工作量很少会超过100。以一个合理的并行度处理你的工作项,例如数据库服务器CPU核心数量加上20%左右。 - usr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接