从 SqlDataReader 读取 JSON 字符串的最有效方式和最快方式

Most efficient way and fastest way to read JSON string from SqlDataReader

我正在尝试从格式为 JSON 的 SQL Server 2016 数据库中 return 一个相当大的数据集(几千行)(使用 FOR JSON AUTO SQL Server 2016 的功能);但是,我 运行 遇到从 SqlDataReader 读取结果的问题。

我之前将结果作为常规行加载到数据表中,效果很好(加载整个 table 大约需要 10-15 秒)。但是,如果我尝试使用相同的数据将字符串 returned 构建为 JSON,则从 DataReader 构建字符串 returned 需要几分钟时间。我想补充一点,我也在这个查询中 returning 了大量的二进制数据(我有试图从数据库中检索的 SqlGeometry 对象)。我从这里 return 编辑的典型字符串有几个 100k 个字符长,从 reader 读取的速度非常慢。

反正我解析JSON的加载代码如下:

public static Task<string> ExecuteJsonReaderAsync(string connectionString, CommandType commandType, string commandText, SqlParameter[] oParams = null, int timeout = 30, CancellationToken token = default(CancellationToken))
    {
        return Task<string>.Factory.StartNew(() =>
        {
            var str = string.Empty;
            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();
                using (var command = new SqlCommand(commandText, connection))
                {
                    command.CommandTimeout = timeout;
                    command.CommandType = commandType;

                    if (oParams?.Length > 0) command.Parameters.AddRange(oParams);

                    using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection))
                    {
                        while (reader.Read())
                        {
                            str = $"{str}{reader[0]}";
                        }
                        reader.Close();
                    }    

                    return str;
                }
            }
        }, token);    
    }

我尝试了各种命令选项来尝试从 CloseConnection、SequentialAccess、SingleResult 加速 运行ging,但无济于事。为什么构建字符串比从相同的数据加载 DataTable 花费的时间长得多,有没有更快的方法来完成这个?

我认为这一定是我做错了什么或者我忽略了,我希望之前有人 运行 解决过这个问题。有什么想法吗?

您的代码在每个循环中重新分配内存中的字符串变量。这不利于代码的性能。相反,class StringBuilder 有一个内部缓冲区,允许更少的内存重新分配,如果您知道数据的总长度,您还可以控制此缓冲区的大小以避免重新分配发生.

所以

// Set an initial capacity of 1MB
StringBuilder str = new StringBuidler(1024*1024);
while (reader.Read())
{
    str.Append(reader[0].ToString());
}

....
return str.ToString();

更多关于 C# string immutable concept here

这里有几个问题:

  1. 您通过 $"{str}{reader[0]}" 使用字符串格式化对 GC 来说性能非常高。您应该使用 StringBuilderAppend() 方法。
  2. 你真的应该像这样一直使用 async

以下是我将如何编写代码以获得更好的性能(通过异步缩放和通过 StringBuilder 在内存方面):

        public static async Task<string> ExecuteJsonReaderAsync(string connectionString, CommandType commandType, string commandText, SqlParameter[] oParams = null, int timeout = 30, CancellationToken token = default(CancellationToken))
        {
            var str = string.Empty;
            await using var connection = new SqlConnection(connectionString);

            await connection.OpenAsync(token);

            await using var command = new SqlCommand(commandText, connection);
            command.CommandTimeout = timeout;
            command.CommandType = commandType;

            if (oParams?.Length > 0) command.Parameters.AddRange(oParams);

            var stringBuilder = new StringBuilder(1024 * 1024);
            await using var reader = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection, token);
            while (await reader.ReadAsync(token))
            {
                stringBuilder.Append(reader[0]);
            }
            await reader.CloseAsync();

            return stringBuilder.ToString();
        }