使用多线程服务的数据库连接池

时间:2012-01-06 18:32:59

标签: c# multithreading service connection-pooling

我有一个使用TPL库进行线程化的.NET 4 C#服务。我们最近将它切换为也使用连接池,因为一个连接正在成为处理的瓶颈。

以前,我们使用lock子句来控制连接对象的线程安全性。当工作备份时,队列将作为任务存在,并且许多线程(任务)将等待lock子句。现在,在大多数情况下,线程会更快地等待数据库IO和工作进程。

但是,现在我正在使用连接池,我们遇到了一个新问题。达到最大连接数(默认值为100)后,如果请求进一步连接,则会超时(请参阅Pooling info)。发生这种情况时,会抛出异常,说“连接请求超时”。

我的所有IDisposable都在using语句中,我正在管理我的连接。这种情况的发生是由于请求的工作量超过了池可以处理的工作量(这是预期的)。我理解为什么抛出这个异常,并且知道处理它的方法。一个简单的重试感觉就像一个黑客。我也意识到我可以通过连接字符串增加超时时间,但这不是一个可靠的解决方案。在之前的设计中(没有池化),工作项将因应用程序内的锁定而处理。

处理此方案以确保处理所有工作的好方法是什么?

3 个答案:

答案 0 :(得分:11)

另一种方法是在代码中使用semaphore来检索池中的连接(希望返回它们)。 sempahore就像一个锁定语句,除了它允许一次配置数量的请求者,而不仅仅是一个。

这样的事情应该做:

//Assuming mySemaphore is a semaphore instance, e.g. 
// public static Semaphore mySemaphore = new Semaphore(100,100);
try {
  mySemaphore.WaitOne(); // This will block until a slot is available.
  DosomeDatabaseLogic();
} finally {
  mySemaphore.Release();
}

答案 1 :(得分:2)

您可以使用Parallel.ForEach()方法控制并行度,如下所示:

var items = ; // your collection of work items
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = 100 };
Parallel.ForEach(items, parallelOptions, ProcessItem)

在这种情况下,我选择将度数设置为100,但您可以选择对当前连接池实现有意义的值。

此解决方案当然假设您预先拥有一系列工作项。但是,如果您通过某些外部机制(如传入的Web请求)创建新任务,则异常实际上是一件好事。此时我建议您使用并发队列数据结构,您可以放置​​工作项并在工作线程可用时弹出它们。

答案 2 :(得分:0)

最简单的解决方案是将连接超时增加到您愿意在返回失败之前阻止请求的时间长度。必须有一段时间“太长”。

这有效地将连接池用作具有超时的工作队列。这比尝试自己实现一个容易得多。您必须检查连接池是否公平(FIFO)。