NHibernate似乎没有对PostgreSQL进行大容量插入
我正在与NHibernate连接PostgreSQL数据库 背景 我做了一些简单的测试…似乎需要2秒来保存300条记录。 我有一个具有相同功能的Perl程序,但使用直接SQL只需要70%的时间。 我不确定这是否是预期的。我认为C/NHiBiNT会更快或至少在PAR上。 问题 我的观察结果之一是(在NHibernate似乎没有对PostgreSQL进行大容量插入,nhibernate,postgresql,insert,bulk,Nhibernate,Postgresql,Insert,Bulk,我正在与NHibernate连接PostgreSQL数据库 背景 我做了一些简单的测试…似乎需要2秒来保存300条记录。 我有一个具有相同功能的Perl程序,但使用直接SQL只需要70%的时间。 我不确定这是否是预期的。我认为C/NHiBiNT会更快或至少在PAR上。 问题 我的观察结果之一是(在show\u sql打开的情况下),NHibernate会发出几百次INSERT,而不是执行处理多行的大容量INSERT。请注意,我自己分配主键,而不是使用“本机”生成器 这是预期的吗?我是否可以让它发
show\u sql
打开的情况下),NHibernate会发出几百次INSERT,而不是执行处理多行的大容量INSERT。请注意,我自己分配主键,而不是使用“本机”生成器
这是预期的吗?我是否可以让它发出批量插入语句?在我看来,这可能是我可以提高性能的一个方面。我还发现NHibernate没有批量插入到PostgreSQL中。 我确定了两个可能的原因: 1) Npgsql驱动程序不支持批插入/更新() 2) NHibernate没有用于PostgreSQL(Npgsql)的*BatchingBatcher(工厂)。我尝试将Devart dotConnect驱动程序与NHibernate一起使用(我为NHibernate编写了自定义驱动程序),但仍然不起作用 我想这个驱动程序也应该实现IEmbeddedBatcherFactoryProvider接口,但它对我来说并不是微不足道的(在Oracle中使用一个接口不起作用;)
有没有人强迫Nhibarnate对PostgreSQL进行批插入,或者可以确认我的结论?正如stachu正确发现的那样:Nhibarnate没有用于PostgreSQL(Npgsql)的*BatchingBatcher(工厂) 正如stachu问的那样:有人强迫Nhibarnate批量插入PostgreSQL吗 我编写了一个批处理程序,它不使用任何Npgsql批处理内容,但可以操作SQL字符串“oldschool style”(插入[…]值(…),(…),…)
使用系统;
使用系统集合;
使用系统数据;
使用系统诊断;
使用系统文本;
使用Npgsql;
名称空间NHibernate.AdoNet
{
公共类PostgressClientBatchingBatcherFactory:IBatcherFactory
{
公共虚拟IBatcher CreateBatcher(ConnectionManager ConnectionManager、IInterceptor拦截器)
{
返回新的PostgressClientBatchingBatcher(连接管理器、拦截器);
}
}
///
///PostgressClientBatchingBatcher的摘要说明。
///
公共类PostgressClientBatchingBatcher:AbstractBatcher
{
私有整数批量大小;
命令的私有整数计数=0;
受影响的私人总收入;
私有StringBuilder命令;
专用int m_参数计数器;
私有IDB命令currentBatch;
公共PostgressClientBatchingBatcher(连接管理器连接管理器,IInterceptor拦截器)
:base(连接管理器、拦截器)
{
batchSize=Factory.Settings.AdoBatchSize;
}
私有字符串NextParam()
{
return“:p”+m_参数计数器++;
}
公共覆盖无效添加批次(预期预期)
{
if(expectation.CanBeBatched&&!(CurrentCommand.CommandText.StartsWith(“插入”)&&CurrentCommand.CommandText.Contains(“值”))
{
//非批处理行为
IDbCommand=CurrentCommand;
LogCommand(当前命令);
int rowCount=ExecuteNonQuery(cmd);
expection.verifyoutcomenonbatch(行数,cmd);
currentBatch=null;
返回;
}
TotalExpectedRowsInfected+=Expected.ExpectedRowCount;
日志信息(“添加到批次”);
int len=CurrentCommand.CommandText.Length;
int idx=CurrentCommand.CommandText.IndexOf(“值”);
int endidx=idx+“值”。长度+2;
如果(currentBatch==null)
{
//开始新的批次。
currentBatch=新的NpgsqlCommand();
sbBatchCommand=新的StringBuilder();
m_参数计数器=0;
string preCommand=CurrentCommand.CommandText.Substring(0,endidx);
sbBatchCommand.Append(预命令);
}
其他的
{
//仅附加值
sbBatchCommand.Append(“,(”);
}
//将CurrentCommand中的值追加到sbBatchCommand
字符串值=CurrentCommand.CommandText.Substring(endidx,len-endidx-1);
//获取所有值
string[]split=values.split(',');
ArrayList paramName=新的ArrayList(split.Length);
for(int i=0;iusing System;
using System.Collections;
using System.Data;
using System.Diagnostics;
using System.Text;
using Npgsql;
namespace NHibernate.AdoNet
{
public class PostgresClientBatchingBatcherFactory : IBatcherFactory
{
public virtual IBatcher CreateBatcher(ConnectionManager connectionManager, IInterceptor interceptor)
{
return new PostgresClientBatchingBatcher(connectionManager, interceptor);
}
}
/// <summary>
/// Summary description for PostgresClientBatchingBatcher.
/// </summary>
public class PostgresClientBatchingBatcher : AbstractBatcher
{
private int batchSize;
private int countOfCommands = 0;
private int totalExpectedRowsAffected;
private StringBuilder sbBatchCommand;
private int m_ParameterCounter;
private IDbCommand currentBatch;
public PostgresClientBatchingBatcher(ConnectionManager connectionManager, IInterceptor interceptor)
: base(connectionManager, interceptor)
{
batchSize = Factory.Settings.AdoBatchSize;
}
private string NextParam()
{
return ":p" + m_ParameterCounter++;
}
public override void AddToBatch(IExpectation expectation)
{
if(expectation.CanBeBatched && !(CurrentCommand.CommandText.StartsWith("INSERT INTO") && CurrentCommand.CommandText.Contains("VALUES")))
{
//NonBatching behavior
IDbCommand cmd = CurrentCommand;
LogCommand(CurrentCommand);
int rowCount = ExecuteNonQuery(cmd);
expectation.VerifyOutcomeNonBatched(rowCount, cmd);
currentBatch = null;
return;
}
totalExpectedRowsAffected += expectation.ExpectedRowCount;
log.Info("Adding to batch");
int len = CurrentCommand.CommandText.Length;
int idx = CurrentCommand.CommandText.IndexOf("VALUES");
int endidx = idx + "VALUES".Length + 2;
if (currentBatch == null)
{
// begin new batch.
currentBatch = new NpgsqlCommand();
sbBatchCommand = new StringBuilder();
m_ParameterCounter = 0;
string preCommand = CurrentCommand.CommandText.Substring(0, endidx);
sbBatchCommand.Append(preCommand);
}
else
{
//only append Values
sbBatchCommand.Append(", (");
}
//append values from CurrentCommand to sbBatchCommand
string values = CurrentCommand.CommandText.Substring(endidx, len - endidx - 1);
//get all values
string[] split = values.Split(',');
ArrayList paramName = new ArrayList(split.Length);
for (int i = 0; i < split.Length; i++ )
{
if (i != 0)
sbBatchCommand.Append(", ");
string param = null;
if (split[i].StartsWith(":")) //first named parameter
{
param = NextParam();
paramName.Add(param);
}
else if(split[i].StartsWith(" :")) //other named parameter
{
param = NextParam();
paramName.Add(param);
}
else if (split[i].StartsWith(" ")) //other fix parameter
{
param = split[i].Substring(1, split[i].Length-1);
}
else
{
param = split[i]; //first fix parameter
}
sbBatchCommand.Append(param);
}
sbBatchCommand.Append(")");
//rename & copy parameters from CurrentCommand to currentBatch
int iParam = 0;
foreach (NpgsqlParameter param in CurrentCommand.Parameters)
{
param.ParameterName = (string)paramName[iParam++];
NpgsqlParameter newParam = /*Clone()*/new NpgsqlParameter(param.ParameterName, param.NpgsqlDbType, param.Size, param.SourceColumn, param.Direction, param.IsNullable, param.Precision, param.Scale, param.SourceVersion, param.Value);
currentBatch.Parameters.Add(newParam);
}
countOfCommands++;
//check for flush
if (countOfCommands >= batchSize)
{
DoExecuteBatch(currentBatch);
}
}
protected override void DoExecuteBatch(IDbCommand ps)
{
if (currentBatch != null)
{
//Batch command now needs its terminator
sbBatchCommand.Append(";");
countOfCommands = 0;
log.Info("Executing batch");
CheckReaders();
//set prepared batchCommandText
string commandText = sbBatchCommand.ToString();
currentBatch.CommandText = commandText;
LogCommand(currentBatch);
Prepare(currentBatch);
int rowsAffected = 0;
try
{
rowsAffected = currentBatch.ExecuteNonQuery();
}
catch (Exception e)
{
if(Debugger.IsAttached)
Debugger.Break();
throw;
}
Expectations.VerifyOutcomeBatched(totalExpectedRowsAffected, rowsAffected);
totalExpectedRowsAffected = 0;
currentBatch = null;
sbBatchCommand = null;
m_ParameterCounter = 0;
}
}
protected override int CountOfStatementsInCurrentBatch
{
get { return countOfCommands; }
}
public override int BatchSize
{
get { return batchSize; }
set { batchSize = value; }
}
}
}