2011-01-06 6 views
6

나는 NHibernate가있는 PostgreSQL 데이터베이스와 인터페이스하고 있습니다.NHibernate가 PostgreSQL에 벌크 삽입을하지 않는 것처럼 보입니다.

배경 나는 (300 개) 기록을 계속 2 초를 복용 보인다 ... 몇 가지 간단한 테스트를했다. 필자는 동일한 기능을 가진 Perl 프로그램을 가지고 있지만 대신 직접 SQL을 발행하며 시간의 70 % 만 사용합니다. 이것이 예상되는지 확실하지 않습니다. 나는 C#/NHibernate가 더 빠를 것이라고 생각했다. 내 관찰

하나는 (show_sql와 함께이 켜져)이다

질문은 NHibernate에 대신 여러 행의 걱정을 대량 삽입을하는, INSERT들에게 수백 시간을 발행한다. 그리고 "기본"생성기를 사용하지 않고 기본 키를 직접 할당합니다.

예상 되나요? 어쨌든 대량 INSERT 문을 대신 발행 할 수 있습니까? 이것이 성능을 향상시킬 수있는 영역 중 하나 일 수 있다고 생각합니다.

+0

하는 경우 당신은 nhibernate가'insert '대신'copy from'을 사용하도록 설득 할 수 있습니다. 그게 펄 프로그램이하는 일일 수도 있습니다. –

답변

2

나는 또한 NHibernate가 PostgreSQL에 일괄 삽입을하지 않는 것을 발견했다. 내가 두 가지 가능성 확인 :

1) Npgsql 드라이버가 배치 삽입을 지원하지 않습니다/NHibernate에가 없습니다

2) 업데이트 (see forum) * BatchingBatcher PostgreSQL를위한 (공장) (Npgsql). 나는 NHibernate (나는 NHibernate에 대한 사용자 정의 드라이버를 썼다)와 Devart dotConnect 드라이버를 사용해 보았지만 여전히 효과가 없었다.

나는이 드라이버는 IEmbeddedBatcherFactoryProvider 인터페이스를 구현해야합니다 생각하지만 (Oracle 용 하나가 작동하지 않았다 사용) 나에게 사소한없는 것 같다)

사람이 PostgreSQL을에 배치 삽입 할 Nhibarnate을 강제로 관리하거나 할 수 했나 내 결론을 확인해? stachu이 askes으로 NHibernate에이 없습니다 * PostgreSQL의 (Npgsql)에 대한 BatchingBatcher (공장) 는 : stachu이 발견으로

6

제대로 사람이 PostgreSQL을

에 배치 삽입 할 Nhibarnate을 강제로 관리나요

는 전 고점을 썼다 어떤 Npgsql 일괄 물건을 사용하지 않지만, SQL 문자열 "oldschool 스타일"을 조작 않습니다 (INSERT INTO [..] VALUES (...) (...), ...)

using 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; } 
     } 
    } 
} 
+1

NHibernate 구성의'adonet.factory_class' 속성을'PostgresClientBatchingBatcherFactory' 클래스의 정규화 된 이름으로 설정하고 물론'adonet.batch_size'를 더 큰 숫자로 설정해야 할 필요가 있음을 언급해야합니다 '0'보다 – Siewers

+0

나는 이것을 시도하고 그것은 작동하지 않습니다. 상태 저장을 종료 한 후에 보류중인 명령을 보내지 않습니다. –

+0

실제로, 그것은 나를 위해 일했습니다. 이 게시물은 오래되었지만 다른 사람에게 도움이 될 수도 있습니다. 일괄 처리 크기가 50 인 9000+ 삽입물의 경우 트랜잭션이 예를 들어 6310ms에서 시작되었습니다. ~ 3385ms. 나는 배치 크기로 조금 더 놀 것이다. 그러나 그래, 그것은 효과가 있었다. –

관련 문제