C# 低负载下与数据库的连接问题(dotnet core)

C# 低负载下与数据库的连接问题(dotnet core),c#,sql-server,linux,.net-core,C#,Sql Server,Linux,.net Core,我正在尝试对一个基本的API进行负载测试,我发现数据库连接出现了一些奇怪的问题。 我现在已经把它缩小到SQL连接本身。(我正在使用选择1仅测试连接) 在非常低的负载(每秒15次呼叫)下,一切工作都完全符合预期 在低负载(每秒25个呼叫)下,前4-5个呼叫以正常速度返回,然后快速减速。由于池中没有连接,大量呼叫超时 在中等负载(每秒50次呼叫)下,所有东西都会完全锁定,不会返回。我开始发现一些奇怪的事情,比如在建立与SQL Server的连接时发生了与网络相关或特定于实例的错误。即将出现。无法再次

我正在尝试对一个基本的API进行负载测试,我发现数据库连接出现了一些奇怪的问题。 我现在已经把它缩小到SQL连接本身。(我正在使用
选择1
仅测试连接)

在非常低的负载(每秒15次呼叫)下,一切工作都完全符合预期

在低负载(每秒25个呼叫)下,前4-5个呼叫以正常速度返回,然后快速减速。由于池中没有连接,大量呼叫超时

在中等负载(每秒50次呼叫)下,所有东西都会完全锁定,不会返回。我开始发现一些奇怪的事情,比如在建立与SQL Server的连接时发生了与网络相关或特定于实例的错误。即将出现。无法再次从池中获取连接

服务器上的exec sp_who2也显示没有来自dotnet的连接

更糟糕的是,从中恢复过来的唯一方法是恢复整个服务

我已经排除了服务器本身,因为这发生在prem上强大的SQL server、azureSql数据库和docker上运行的本地服务上

 int selected = 0;
 var timer = Stopwatch.StartNew();
 using (SqlConnection connection = CreateNewConnection())
 {
      try
      {
          connection.Open();
          selected = connection.QueryFirst<int>("SELECT 1");
          timer.Stop();
      }
      catch (Exception e)
      {
          Console.WriteLine("Failed connection");
          Console.WriteLine("fatal    " + e.Message);
          responseBuilder.AddErrors(e);
      }
      finally
      {
          connection.Close();
      }
 }

 responseBuilder.WithResult(new {selected, ms = timer.ElapsedMilliseconds});
另一个音符

不使用喷枪(等待上一个调用完成,然后再发送另一个调用)似乎具有足够的吞吐量。这似乎与同时处理太多请求有关

版本信息 dotnet
2.1.401

SqlClient
4.5.1

我可以验证是否发生了可疑的事情,但可能不是池。我创建了一个控制台应用程序,并在同一个框上从Windows控制台和WSL控制台运行它。通过这种方式,我可以从同一个客户端但不同的OS/运行时运行相同的代码

在Windows上,即使使用荒谬的500 DOP,每个连接所需的时间也不到一毫秒:

985 : 00:00:00.0002307
969 : 00:00:00.0002107
987 : 00:00:00.0002270
989 : 00:00:00.0002392
WSL中的相同代码需要8秒或更长时间,即使DOP为20!较大的DOP值会导致超时。10将产生类似于Windows的结果

一旦我禁用了MARS,性能恢复正常:

983 : 00:00:00.0083687
985 : 00:00:00.0083759
987 : 00:00:00.0083971
989 : 00:00:00.0083938
992 : 00:00:00.0084922
991 : 00:00:00.0045206
994 : 00:00:00.0044566
这仍然比直接在Windows上运行要慢20倍,但在您并排检查数字之前,几乎无法察觉

这是我在这两种情况下使用的代码:

   static void Main(string[] args)
    {
        Console.WriteLine("Starting");

        var options=new ParallelOptions { MaxDegreeOfParallelism = 500 };
        var watch=Stopwatch.StartNew();
        Parallel.For(0,1000,options,Call);
        Console.WriteLine($"Finished in {watch.Elapsed}");
    }

    public static void Call(int i)
    {            
        var watch = Stopwatch.StartNew();

        using (SqlConnection connection = CreateNewConnection())
        {
            try
            {
                connection.Open();
                var cmd=new SqlCommand($"SELECT {i}",connection);
                var selected =cmd.ExecuteScalar();                    
                Console.WriteLine($"{selected} : {watch.Elapsed}");
            }
            catch (Exception e)
            {
                Console.WriteLine($"Ooops!: {e}");
            }
        }
    }

    private static SqlConnection CreateNewConnection()
    {
        var builder = new SqlConnectionStringBuilder()
        {
            UserID = "someUser",
            Password = "somPassword",                        
            InitialCatalog = "tempdb",
            DataSource = @"localhost",
            MultipleActiveResultSets = true,
            Pooling=true //true by default                
            //MaxPoolSize is 100 by default
        };
        return new SqlConnection(builder.ConnectionString);
    }
}

需要指出的是,这些都是枪声。所以,批处理会尽快启动,然后等待每个请求返回。似乎DBContext是为调用创建的&在整个会话中保持不变。您是否可以显示您知道如何连接数据库和连接哪个服务器的代码,这些代码是每一天持久化或实例化的call@StevenDall更新问题,不要添加评论。至于缺少什么,我想说你还没有提供所有的代码,但是看起来
CreateConnection
正在泄漏连接。使用了连接池,因此不需要重新打开与服务器的实际连接。当您调用
Close()
或使用
块退出
时,连接将重置并放回池中。如果连接实际已关闭,则不会出现这样的池错误。PS:您不需要这里的
.Close()
,当执行离开
using
块时,它将被调用。@StevenDall您没有提到您使用的是什么OS或.NET核心版本。Linux的SqlClient库中可能存在错误。另一方面,连接池不是新的,当您考虑重用相同的连接时,25调用/SEC是非常低的。除非连接字符串禁用连接池,否则存在一个影响所有操作系统的错误,并且在SqlClient 4.5.0中已修复。也许这是一个回归,或者是一个新的错误?讨论表明该问题是由于线程池使用不当造成的。驱动程序被卡住而没有实际打开连接,这就解释了为什么在服务器端看不到任何连接。您可能应该创建一个最小的程序,并尝试在Windows上使用它。不幸的是,我需要MARS,但我可以暂时关闭它。我看看是否有用,谢谢。@StevenDall为什么需要MARS?我们有几个连接以异步方式运行,同时返回数据。每个http调用。但我们也有一些事务,其中一些初始查询同时运行,以准备使用它们的最后几个查询。由于需要全部回滚,所以出现了一些问题。尝试同时执行其中一些操作以节省通话时间。关闭了火星,一切都像测试中预期的那样工作。我们将改变需要改变的内容,以解决这一问题now@StevenDall您不(不应该)需要MARS来使用异步查询。同样,在.NETCore中可能有一些bug迫使您使用它。通常,如果要在同一个连接上触发多个异步查询,就需要MARS。这是一个相当冒险的想法。您不需要它来启动事务,一个接一个地运行几个异步查询,然后提交或回滚事务
   static void Main(string[] args)
    {
        Console.WriteLine("Starting");

        var options=new ParallelOptions { MaxDegreeOfParallelism = 500 };
        var watch=Stopwatch.StartNew();
        Parallel.For(0,1000,options,Call);
        Console.WriteLine($"Finished in {watch.Elapsed}");
    }

    public static void Call(int i)
    {            
        var watch = Stopwatch.StartNew();

        using (SqlConnection connection = CreateNewConnection())
        {
            try
            {
                connection.Open();
                var cmd=new SqlCommand($"SELECT {i}",connection);
                var selected =cmd.ExecuteScalar();                    
                Console.WriteLine($"{selected} : {watch.Elapsed}");
            }
            catch (Exception e)
            {
                Console.WriteLine($"Ooops!: {e}");
            }
        }
    }

    private static SqlConnection CreateNewConnection()
    {
        var builder = new SqlConnectionStringBuilder()
        {
            UserID = "someUser",
            Password = "somPassword",                        
            InitialCatalog = "tempdb",
            DataSource = @"localhost",
            MultipleActiveResultSets = true,
            Pooling=true //true by default                
            //MaxPoolSize is 100 by default
        };
        return new SqlConnection(builder.ConnectionString);
    }
}