【问题标题】:Can Dapper release database connection early?Dapper 可以提前释放数据库连接吗?
【发布时间】: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 毫秒创建一千个对象时保持连接打开?

标签: c# dapper


【解决方案1】:

感谢cmets

正如指出我的测量不正确,因为 ExecuteReaderAsync 没有读取结果

修改我的测量以更正确地包括从 DB2 中实际读取结果,这表明几乎所有时间都花在了读取结果上。不在 Dappers 中解压

            if (command.Buffered)
            {
                var buffer = new List<T>();
                var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
                var swReadAsyncAndUnpack = Stopwatch.StartNew();
                var swReadAsync = Stopwatch.StartNew();
                while (await reader.ReadAsync(cancel).ConfigureAwait(false))
                {
                    swReadAsync.Stop();
                    object val = func(reader);
                    buffer.Add(GetValue<T>(reader, effectiveType, val));
                    swReadAsync.Start();
                }
                swReadAsync.Stop();
                swReadAsyncAndUnpack.Stop();
                Console.WriteLine($" ### All ReadAsync took {swReadAsync.ElapsedMilliseconds}");
                Console.WriteLine($" ### All ReadAsync and unpack took {swReadAsync.ElapsedMilliseconds}");
                while (await reader.NextResultAsync(cancel).ConfigureAwait(false)) { /* ignore subsequent result sets */ }
                command.OnCompleted();
                return buffer;
            }

我认为我们需要让 IBM 参与其中,因为在 IBM.Data.DB2.Core 中使用 ReadAsync 所花费的时间与我们的 DBA 可以在 DB2 中测量的运行时间不匹配

【讨论】:

  • 在 DB2 内 听起来那里没有任何网络传输时间 - 我会确保您的 DBA 也会带来苹果供您比较:)
猜你喜欢
  • 1970-01-01
  • 2011-02-15
  • 2020-12-16
  • 2017-12-24
  • 2016-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-13
相关资源
最近更新 更多