【发布时间】:2021-12-28 20:00:20
【问题描述】:
我们正在使用 Dapper 在大型机上的 IBM Db2 中执行 SQL 我认为我们执行 SQL 的代码非常标准。从连接池中获取连接,使用 Dapper 执行 SQL。然后释放与池的连接
using var connection = GetConnection();
{
return await connection.QueryAsync<dynamic>(sql, new { instrumentIds });
}
问题在于,虽然 DB2 只需要几毫秒来执行 SQL,但 Dapper 将使用大约 100 毫秒来解压缩数千行,每行可能有 20 列
因此,当 Dapper 解包结果时,连接也被锁定,最终我们的应用程序急需与 DB2 的连接
如果在 DB2 上执行 SQL 时 Dapper 可以释放连接,并且在 Dapper 解包结果之前,我们可以将可以处理的请求增加十倍而不增加连接
这可能吗?
我已尝试在 Dapper 中围绕 ExecuteReaderAsync 调用添加经过时间记录,我可以确认此调用仅使用 10 毫秒,而整个 QueryAsync 调用使用 140 毫秒
private static Task<DbDataReader> ExecuteReaderWithFlagsFallbackAsync(DbCommand cmd, bool wasClosed, CommandBehavior behavior, CancellationToken cancellationToken)
{
var sw = Stopwatch.StartNew();
var task = cmd.ExecuteReaderAsync(GetBehavior(wasClosed, behavior), cancellationToken);
sw.Stop();
Console.WriteLine($" ### ExecuteReaderAsync took {sw.ElapsedMilliseconds}");
if (task.Status == TaskStatus.Faulted && Settings.DisableCommandBehaviorOptimizations(behavior, task.Exception.InnerException))
{ // we can retry; this time it will have different flags
return cmd.ExecuteReaderAsync(GetBehavior(wasClosed, behavior), cancellationToken);
}
return task;
}
【问题讨论】:
-
您能否找到Dapper source code 中存在问题的“保持连接时间过长”部分,以便我们检查/提交问题?
-
@Claus 我添加了代码以在 Dapper 代码中显示我的测量结果。这能回答你的问题吗?
-
ExecuteReaderAsync不传输数据,据我所知。但是数据传输部分显然需要连接仍然是打开的。您是否尝试过读取所有数据但将其丢弃,以证明问题实际上是 Dapper 反序列化? -
在 Dapper 解压结果之前 - 引用 repo 中的自述文件:缓冲与非缓冲阅读器 Dapper 的默认行为是执行您的 SQL 并在返回时缓冲整个阅读器.这在大多数情况下是理想的,因为它最大限度地减少了数据库中的共享锁并减少了数据库网络时间。但是,在执行大量查询时,您可能需要最小化内存占用并仅根据需要加载对象。为此,将 buffered: false 传递到 Query 方法中。
-
DB2 只需几毫秒即可执行 SQL - 这可能是真的,但执行 sql 和准备结果集是一回事,将其拖到网络上完全是另一回事。 Dapper 将使用大约 100 毫秒来解压缩数千行,每行可能有 20 列 - 所以至少有 40,000 个数据单元(数千个 20 倍),数据长度未知(告诉我们列?)- 你有多确定 Dapper 在 100 毫秒内下载了所有数据,例如 10 毫秒,然后在另一个 90 毫秒创建一千个对象时保持连接打开?