我有一个 C# 函数可以在 postgresql 中进行批量插入。这里发生的基本上是我有一个通用实体数组,它将被转换成一个 DataTable,然后与 BinaryImport 一起使用将数据插入到我的 postgres 数据库中。批量插入后,我从数据库读回插入的数据,并用该数据填充实体(我使用临时列来确定是否已使用最后一次批量插入插入表的一行)。
这工作得很好,直到我尝试批量插入大约 1.000.000 (978.767) 行。 (之前我只使用了〜500.000行)
大约 4 分钟后,我收到以下异常:
Npgsql.NpgsqlException:从流读取时出现异常 ---> System.TimeoutException:读取尝试期间超时
异常发生在我读回插入数据的部分的
while(reader.Read())
行。
这是我的代码:
public IEnumerable<TEntity> InsertBulkReader<TEntity>(TEntity[] entities)
{
var table = ClientEntity.GetTable<TEntity>();
var nonIdColumns = table.Columns.Where(column => !column.IsIdentity);
var timeStampColumn = new Column() { Name = "TMP_BULK_INSERT_TIME_STAMP", PgType = NpgsqlDbType.TimestampTz };
//Adding timeStampColumn to the actual table
using (var connection = new NpgsqlConnection(base.ConnectionString))
{
connection.Open();
using (var transactionScope = TransactionScopeUtil.Create(Transaction.Current))
{
var cmd = $"COPY {table.FullName()} ({string.Join(",", nonIdColumns.Concat(new List<Column>() { timeStampColumn }).Select(column => column.Name))}) FROM STDIN BINARY";
using (var writer = connection.BeginBinaryImport(cmd))
{
writer.Timeout = TimeSpan.FromMinutes(10);
foreach (DataRow row in dataTable.Rows)
{
writer.StartRow();
foreach (var column in nonIdColumns)
{
if (column.PgType == NpgsqlDbType.Unknown || column.PgType == (NpgsqlDbType.Array | NpgsqlDbType.Unknown))
{
writer.Write(row[column.Name]);
}
else
{
writer.Write(row[column.Name], column.PgType);
}
}
writer.Write(row[timeStampColumn.Name], timeStampColumn.PgType);
}
writer.Complete();
writer.Close();
}
using (var command = connection.CreateCommand())
{
command.CommandTimeout = 0;
command.CommandText = $"SELECT * FROM {table.FullName()} WHERE {timeStampColumn.Name} >= @TIMESTAMP";
command.Parameters.Add(new NpgsqlParameter() { ParameterName = "TIMESTAMP", Value = timeStampBeforeInsert, });
using (var reader = command.ExecuteReader())
{
while (reader.Read()) //Here the exception is thrown
{
var entity = new TEntity();
foreach (var column in table.Columns)
{
//Populate entity with values from reader
}
result.Add(entity);
}
}
}
transactionScope.Complete();
transactionScope.Dispose();
}
}
return result;
}
我在连接字符串中尝试了以下选项:
CommandTimeout = 0;
InternalCommandTimeout = 0;
KeepAlive = 60 * 10;
我还添加了
command.CommandTimeOut = 0;
到与 ExecuteReader()
一起使用来读回数据的命令。
我进一步检查了postgresql服务器的STATMENT_TIMEOUT,以确保服务器本身没有超时。
显示 STATEMENT_TIMEOUT => "0"
如果我在服务器上执行类似
select pg_wait(60 * 10)
的操作,则不会出现错误,问题一定出在我的代码上。我还能做什么来跟踪问题?
我已经执行了收到异常时正在运行的查询,并在我的 phpstorm 数据库插件中执行了它。两秒后结果显示。但它是分页的,所以我点击了“到最后一页”按钮。然后 30 秒后我收到以下错误
[2023-09-28 15:42:35] Error unmarshaling return header; nested exception is:
[2023-09-28 15:42:35] java.net.SocketException: Connection reset
之后我第二次尝试,又出现了错误
[2023-09-28 15:44:25] Database client process needs more memory to perform the request.
[2023-09-28 15:44:25] To configure settings, open 'development_db@localhost' data source properties, go to the 'Advanced' tab, and add
[2023-09-28 15:44:25] '-XmxNNNm' to the 'VM options' field, where NNN is the number of megabytes (for example, '-Xmx2048m').
按照上面消息中所述将内存增加到 2048mb 后,错误消失了。