2011-01-06 19 views
6

'a Toplu Ekleme Yapmıyor Görünüşe göre, NHibernate ile bir PostgreSQL veritabanı ile arayüzeyim.NHibernate, PostgreSQL

Arkaplan buna 300 kayıtları devam etmek 2 saniye alıyor görünüyor ... bazı basit testler yaptı. Aynı işlevselliğe sahip bir Perl programım var, ancak bunun yerine doğrudan SQL yayınla, zamanın sadece% 70'ini alıyor. Bunun beklenip beklenmediğinden emin değilim. C#/NHibernate daha hızlı ya da en azından eşit olacağını düşündüm. Benim gözlem

One (show_sql açık olan) olmasıdır

Sorular NHibernate yerine çoklu satır umurunda almak toplu INSERT yapmanın, Eklemeler birkaç yüz kez yayımlamaktadır. Ve "ana" jeneratörü kullanmadan birincil anahtarı kendim atadığımı not et.

Bu beklenen mi? Zaten onun yerine toplu INSERT bildirimi yapabildiğim var mı? Bana göre bu, performansı hızlandırabileceğim alanlardan biri olabilir.

+0

ise “insert” yerine 'copy from' kullanmak için nhibernate'i ikna edebilirsiniz, büyük olasılıkla büyüklük sırasını daha hızlı çalıştıracaktır. Perl programının yaptığı şey bu olabilir. –

cevap

2

Ayrıca NHibernate'in PostgreSQL'e batch insert yapmıyor olduğunu da öğrendim. iki olası nedenler tespit:

1) Npgsql sürücü toplu ekler desteklemez/NHibernate bulunmamaktadır

2) güncellemeleri (see forum) * BatchingBatcher PostgreSQL için (fabrika) (Npgsql). NHibernate (NHibernate için özel bir sürücü yazdım) ile Devart dotConnect sürücüsünü kullanmayı denedim ama yine de işe yaramadı.

ben bu sürücü de IEmbeddedBatcherFactoryProvider arabirimini uygulamalıdır varsayalım, ama (Oracle için bir amele vermedi kullanarak;) benim için önemsiz değildir görünüyor)

herkes PostgreSQL toplu ekleme yapılabilinir Nhibarnate zorlamak için yönetilen veya can mü sonucumu onaylayın Stachu askes gibi NHibernate yok * PostgreSQL (Npgsql) için BatchingBatcher (Fabrika) : Stachu öğrendim gibi

6

doğru herkes PostgreSQL

toplu ekleme yapılabilinir Nhibarnate zorlamak başardı mü

Bir dozatörler yazdığı herhangi Npgsql beton malzeme kullanmaz, ancak SQL string "oldschool tarzı" manipüle etmez (INSERT INTO [..] DEĞERLERİ (...), (...), ...)

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

Sözünü etmeliyim ki, 'adonet.factory_class' özelliğini NHibernate yapılandırmasında' PostgresClientBatchingBatcherFactory 'sınıfının tam adıyla ayarlamanız ve tabiki' adonet.batch_size 'değerini daha büyük bir sayıya ayarlamanız gerekir. 0'dan daha. – Siewers

+0

Bunu denedim ve çalışmıyor. Statelessession'ı kapattıktan sonra bekleyen komutları göndermez. –

+0

Aslında, benim için çalıştı. Bu yazının eski olduğunu biliyorum ama başkasına yardım edebilir. Toplu iş boyutu 50'ye eşit olan 9000+ Eklenti ile işlem, örneğin 6310 ms'den alındı. 3385 ms. Parti boyutuyla biraz daha oynayacağım, ama işe yaradı. –