在低负载下连接到数据库的问题(dotnet 核心)



我正在尝试加载测试一个基本的API,我开始从数据库连接中遇到一些奇怪的问题。 现在,我已将其缩小到SQL连接本身。(我只使用SELECT 1来测试连接(

在非常低的负载(每秒 15 次调用(下,一切都完全按预期工作。

在低负载(每秒 25 次调用(下,前 4-5 个调用以适当的速度返回,然后迅速减慢。由于池中没有连接,许多调用超时。

在中等负载(每秒 50 次调用(下,一切都完全锁定,没有任何返回。我开始遇到奇怪的事情,比如A network-related or instance-specific error occurred while establishing a connection to SQL Server.出现。无法再次从池中获取连接。

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

更糟糕的是,从中恢复的唯一方法是退回整个服务。

我已经排除了服务器本身,因为这发生在强大的本地SQL服务器,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核心,并且dapper(即使没有dapper,我也会遇到同样的问题(

我还尝试将最大连接池限制提高到荒谬的数字,例如 1000,但没有效果。

编辑

在尝试了更多之后,我决定尝试使用Postgres。在每秒超过 1k 次调用时完美运行。 我在 sql 服务器本身中缺少某些内容吗?还是在连接上?

需要指出的是,这些是霰弹枪呼叫。因此,批处理会尽快触发,然后等待每个请求返回。

这也是使用 linux(环境是 docker k8s(

有人想知道连接是如何创建的

private IDbConnection CreateNewConnection()
{
var builder = new SqlConnectionStringBuilder()
{
UserID = "sa",
Password = "012Password!",
InitialCatalog = "test",
DataSource = "localhost",
MultipleActiveResultSets = true,
MaxPoolSize = 1000
};
return new SqlConnection(builder.ConnectionString);
}

另一个注意事项

不开枪(等待上一个调用完成,然后再发送另一个调用(似乎具有足够不错的吞吐量。这似乎是同时处理太多请求的问题

版本信息dotnet2.1.401SqlClient4.5.1

我可以验证正在发生一些可疑的事情,但它可能没有汇集。我创建了一个控制台应用程序,并在同一本机上从 Windows 控制台和 WSL 控制台运行它。这样,我就可以从相同的客户端运行相同的代码,但操作系统/运行时不同。

在 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);
}
}

最新更新