从 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
这里有几个问题:
- 您通过
$"{str}{reader[0]}"
使用字符串格式化对 GC 来说性能非常高。您应该使用 StringBuilder
和 Append()
方法。
- 你真的应该像这样一直使用
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();
}
我正在尝试从格式为 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
这里有几个问题:
- 您通过
$"{str}{reader[0]}"
使用字符串格式化对 GC 来说性能非常高。您应该使用StringBuilder
和Append()
方法。 - 你真的应该像这样一直使用
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();
}