我正忙着将使用 Postgres 数据库 v9 的旧系统升级到 v14, 我需要在 C# .NetFramework 4.6.1 应用程序上将 Npgsql 库从当前版本 2.2.7 更新到 4.1.12。
在我的测试中,我注意到,如果我使用 Npgsql v3 或更高版本,则使用 NpgsqlDataReader 执行函数并将响应读取到数组中的命令需要花费 10 倍的时间。
代码看起来遵循 while (dbReader.Read()) 循环: 当连接到 Postgres v9 数据库时,运行 Npgsql v2.2.7 需要 10 秒才能运行完 20000 行。 当连接到 Postgres v9 或 v14 数据库时,运行 Npgsql v3 或更高版本需要 140 秒才能运行 20000 行(数据库没有区别)。
using Npgsql;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
namespace UpgradeV2.Sample
{
internal class SampleRequest
{
private static string connectionstring = "server=myhostname;port=5432;database=mydatabase;user id=myuser;password=mypassword;enlist=true;pooling=false;minpoolsize=1;maxpoolsize=100;timeout=1024;commandtimeout=3000;";
public static IEnumerable<DataClass> GetData(long id)
{
var parameters = new Dictionary<string, object>
{
{"i_id", (int)id}
};
using (NpgsqlConnection _npgsqlConnection = new NpgsqlConnection(connectionstring))
{
var command = new NpgsqlCommand
{
CommandType = CommandType.StoredProcedure,
Connection = _npgsqlConnection,
CommandText = "public.function_returning_lots_of_data"
};
foreach (var parameter in parameters)
{
command.Parameters.Add(new NpgsqlParameter { ParameterName = parameter.Key, Value = parameter.Value });
}
using (var dbReader = command.ExecuteReader())
{
var dataList = new Collection<DataClass>();
// to complete this while loop with npgsql v2 takes 10 seconds
// to complete this while loop with npgsql v3-v6 takes 140 seconds
while (dbReader.Read())
{
if (dbReader["id"] == DBNull.Value)
{
continue;
}
DataClass item = new DataClass()
{
id = dbReader.GetFieldValue<int>("id"),
//And about 20 other Fields read using GetFieldValue
};
dataList.Add(item);
}
return dataList;
}
}
}
}
}
我尝试了 Npgsql 库的不同较新版本,从版本 3 开始,所有这些都会导致相同的问题。 我尝试将连接字符串更新为“读取缓冲区大小”的更高值,但没有任何改进 我尝试使用 ExecuteAsync 和 ReadAsync 方法,但几乎没有效果。
我知道这不是最佳解决方案,但它显着提高了将结果读入数组的速度,所以我希望这可以帮助某人,直到找到更优雅的解决方案。
为此,我创建一个数据表,并使用 load 方法将整个结果读入数据表,然后创建一个 DataTableReader 来代替 NpgsqlDataReader。
使用这个 dbReader.Read() 循环将在 5 秒内完成 20000 条记录(在我的例子中是这样)。下面的代码将花费大约 10 秒来填充数据表并创建适配器。 它仍然不如 Npgsql v2.2.7 库那么快,但比 140 秒的加载时间要好得多。
DataTable dataTable = new DataTable();
dataTable.Load(command.ExecuteReader());
return dataTable.CreateDataReader();
请注意: 这是我的具体问题的解决方案,我知道应该重构问题中的原始代码,以针对更现代的方法进行优化,但是,这只是最终将退役的大型系统遗留系统的一小部分,在这种情况下这种情况发生在数百个类+文件中。 因此,针对性能问题的快速通用修复是我现阶段唯一的选择。